package io.itit.ecp.admin.provide.action.controller.visualizing;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import icu.mhb.mybatisplus.plugln.extend.Joins;
import io.itit.ecp.admin.common.constants.ParamConstants;
import io.itit.ecp.admin.common.enums.*;
import io.itit.ecp.admin.provide.action.resp.CarbonCCERStaticsResp;
import io.itit.ecp.admin.provide.action.resp.CarbonEmissionDataActivityResp;
import io.itit.ecp.admin.provide.action.resp.CarbonQuotaStaticsResp;
import io.itit.ecp.admin.provide.action.resp.EmissionIntensityDataResp;
import io.itit.ecp.admin.provide.action.resp.largescreen.*;
import io.itit.ecp.admin.server.entity.*;
import io.itit.ecp.admin.server.entity.extend.EnergyDataExtEntity;
import io.itit.ecp.admin.server.service.*;
import io.itit.grass.common.provide.action.resp.ResultResp;
import io.itit.grass.common.provide.action.resp.SelectedResp;
import io.itit.grass.common.server.entity.BaseEntity;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 大屏统计
 */
@Api(tags = "大屏统计")
@RestController
@RequestMapping("/ecp-admin/largescreen")
@RequiredArgsConstructor
public class LargeScreenController {

    @Resource
    private IEnergyDataService energyDataService;

    @Resource
    private ICarbonSinkService carbonSinkService;

    @Resource
    private ICarbonSinkDataService carbonSinkDataService;

    @Resource
    private ISceneProjectService sceneProjectService;

    @Resource
    private ISceneEdificeService sceneEdificeService;

    @Resource
    private IEnergyConsumptionTypeService energyConsumptionTypeService;

    @Resource
    private ICarbonQuotaService carbonQuotaService;

    @Resource
    private ICarbonCCERService carbonCCERService;

    @Resource
    private IDeviceService deviceService;

    @Resource
    private IMeterDeviceService meterDeviceService;

    @Resource
    private IProductTypeService productTypeService;

    @Resource
    private ISceneFloorService sceneFloorService;

    @Resource
    private ISceneRoomService sceneRoomService;

    @Resource
    private ICarbonEmissionRangeService carbonEmissionRangeService;

    @Resource
    private ICarbonPriceDataService carbonPriceDataService;

    @Resource
    private IActivityCarbonEmissionService activityCarbonEmissionService;

    @Resource
    private IDataFillingDataService dataFillingDataService;

    @Resource
    private IRunningModeDataService runningModeDataService;

    @Resource
    private IEnergyForecastDataService energyForecastDataService;

    @Resource
    private ICarbonRevenueForecastDataService carbonRevenueForecastDataService;

    @Resource
    private IPhysicalModelService physicalModelService;

    @Resource
    private IEnergyCategoryService energyCategoryService;

    /**
     * 楼栋数据
     *
     * @param sceneProjectId projectId
     * @param type           0：总览；1：能源分析；2：碳分析；3：AI预测
     * @param startTime      开始时间
     * @param endTime        结束时间
     */
    @ApiOperation("楼栋数据")
    @GetMapping("edifice/usage")
    public ResultResp<Map<String, Object>> floorUsage(@RequestParam(required = false) String sceneProjectId,
                                                      @RequestParam(required = false) String sceneEdificeId,
                                                      @RequestParam(required = false) String sceneFloorId,
                                                      @RequestParam String type,
                                                      @RequestParam(required = false) String startTime,
                                                      @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<SceneEdificeEntity> entities = sceneEdificeService.lambdaQuery()
                .eq(StringUtils.hasLength(sceneProjectId), SceneEdificeEntity::getSceneProjectId, sceneProjectId)
                .list();
        List<EdificeUsageResp> usageData = Collections.emptyList();
        if (!CollectionUtils.isEmpty(entities)) {
            switch (type) {
                case "0":
                    usageData = queryOverviewFloorUsage(entities, start, end);
                    break;
                case "1":
                    usageData = queryEnergyAnalysisFloorUsage(entities, start, end);
                    break;
                case "2":
                    usageData = queryCarbonAnalysisFloorUsage(entities, sceneEdificeId, sceneFloorId, start, end);
                    break;
                case "3":
                    usageData = queryAIPredictionFloorUsage(entities, start, end);
                    break;
            }
        }
        return ResultResp.success(Collections.singletonMap("cityListValue", usageData));
    }

    private List<EdificeUsageResp> queryAIPredictionFloorUsage(List<SceneEdificeEntity> entities, LocalDateTime startTime, LocalDateTime endTime) {
        BigDecimal t = new BigDecimal("1000");
        List<EdificeUsageResp> list = new ArrayList<>(entities.size());
        for (SceneEdificeEntity edificeEntity : entities) {
            EdificeUsageResp resp = new EdificeUsageResp();
            resp.setName(edificeEntity.getEdificeName());
            resp.setEdificeId(edificeEntity.getId());
            resp.setPosX(edificeEntity.getPosX());
            resp.setPosY(edificeEntity.getPosY());
            resp.setPopPosition(edificeEntity.getPopPosition());

            BigDecimal carbonValue = runningModeDataService.sumCarbonValue(edificeEntity.getId(), startTime, endTime);
            resp.setData(new ArrayList<>(Arrays.asList(
                    new EdificeUsageResp.EdificeUsageData()
                            .setTitle("发电量预测精度")
                            // 管理后台-数据管理-能源预测数据-预测精度相加（统计类型为：发电量）
                            .setValue(energyForecastDataService.sumForecastPrecision(edificeEntity.getId(),
                                    Collections.singletonList(MeterDeviceStaticsTypeEnums.POWER_GENERATION.getValue()),
                                    startTime, endTime))
                            .setIcon("%"),
                    new EdificeUsageResp.EdificeUsageData()
                            .setTitle("用电量预测精度") // 管理后台-数据管理-能源预测数据-预测精度相加（统计类型为：用电量）
                            .setValue(energyForecastDataService.sumForecastPrecision(edificeEntity.getId(), Collections.singletonList(MeterDeviceStaticsTypeEnums.POWER_CONSUMPTION.getValue()), startTime, endTime))
                            .setIcon("%"),
                    new EdificeUsageResp.EdificeUsageData()
                            .setTitle("低碳模式碳排放")
                            .setValue(carbonValue.compareTo(t) >= 0 ? carbonValue.divide(t, 2, RoundingMode.HALF_UP) : carbonValue)
                            .setIcon(carbonValue.compareTo(t) >= 0 ? "tCO2" : "kgCO2")
                    ,
                    new EdificeUsageResp.EdificeUsageData()
                            .setTitle("电网互动收益")
                            .setValue(runningModeDataService.sumIncomeValue(edificeEntity.getId(), startTime, endTime))
                            .setIcon("元")
                    ,
                    new EdificeUsageResp.EdificeUsageData()
                            .setTitle("经济模式费用")
                            .setValue(runningModeDataService.sumSaveElectricityCostsValue(edificeEntity.getId(), startTime, endTime))
                            .setIcon("元")
            )));
            list.add(resp);
        }
        return list;
    }

    private List<EdificeUsageResp> queryCarbonAnalysisFloorUsage(List<SceneEdificeEntity> entities, String sceneEdificeId,
                                                                 String sceneFloorId,
                                                                 LocalDateTime startTime, LocalDateTime endTime) {
        List<EdificeUsageResp> list = new ArrayList<>(entities.size());
        List<String> edificeIds = StringUtils.hasLength(sceneEdificeId) ? Collections.singletonList(sceneEdificeId) :
                entities.stream().map(BaseEntity::getId).collect(Collectors.toList());
        // 碳排放
        List<EnergyDataEntity> dataEntities = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer -> consumer
                        .add("ecp_tt_energy_data.scene_edifice_id", "sceneEdificeId", false)
                        .add("SUM(ecp_tt_energy_data.carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .in(EnergyDataEntity::getSceneEdificeId, edificeIds)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(startTime), EnergyDataEntity::getSolidifyDate, startTime)
                .le(Objects.nonNull(endTime), EnergyDataEntity::getSolidifyDate, endTime)
                .groupBy(EnergyDataEntity::getSceneEdificeId)
                .joinList(EnergyDataEntity.class);

        // 碳减
        List<EnergyDataEntity> reductionData = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer -> consumer
                        .add("ecp_tt_energy_data.scene_edifice_id", "sceneEdificeId", false)
                        .add("SUM(ecp_tt_energy_data.carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .in(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .in(EnergyDataEntity::getSceneEdificeId, edificeIds)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(startTime), EnergyDataEntity::getSolidifyDate, startTime)
                .le(Objects.nonNull(endTime), EnergyDataEntity::getSolidifyDate, endTime)
                .groupBy(EnergyDataEntity::getSceneEdificeId)
                .joinList(EnergyDataEntity.class);
        BigDecimal t = new BigDecimal("1000");
        for (SceneEdificeEntity edificeEntity : entities) {
            EdificeUsageResp resp = new EdificeUsageResp();
            resp.setName(edificeEntity.getEdificeName());
            resp.setEdificeId(edificeEntity.getId());
            resp.setPosX(edificeEntity.getPosX());
            resp.setPosY(edificeEntity.getPosY());
            resp.setPopPosition(edificeEntity.getPopPosition());
            BigDecimal carbonEmission = dataEntities.stream()
                    .filter(item -> StringUtils.pathEquals(item.getSceneEdificeId(), edificeEntity.getId()))
                    .findFirst()
                    .map(EnergyDataEntity::getCarbonEmission).orElse(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_DOWN);

            BigDecimal reductionDataValue = reductionData.stream()
                    .filter(item -> StringUtils.pathEquals(item.getSceneEdificeId(), edificeEntity.getId()))
                    .findFirst()
                    .map(EnergyDataEntity::getCarbonEmission).orElse(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_DOWN);

            BigDecimal carbonSink = carbonSinkDataService.computeCarbonReductionTotal(edificeEntity.getSceneProjectId(), edificeEntity.getId(), sceneFloorId, startTime, endTime);
            resp.setData(new ArrayList<>(Arrays.asList(
                    new EdificeUsageResp.EdificeUsageData()
                            .setTitle("碳排放量")
                            .setValue(carbonEmission.compareTo(t) >= 0 ? carbonEmission.divide(t, RoundingMode.HALF_DOWN) : carbonEmission)
                            .setIcon(carbonEmission.compareTo(t) >= 0 ? "tCO2" : "kgC02"),
                    new EdificeUsageResp.EdificeUsageData()
                            .setTitle("碳减排量")
                            .setValue(reductionDataValue.compareTo(t) >= 0 ? reductionDataValue.divide(t, RoundingMode.HALF_DOWN) : reductionDataValue)
                            .setIcon(reductionDataValue.compareTo(t) >= 0 ? "tCO2" : "kgC02"),
                    new EdificeUsageResp.EdificeUsageData()
                            .setTitle("碳汇量")
                            .setValue(carbonSink.compareTo(t) >= 0 ? carbonSink.divide(t, RoundingMode.HALF_DOWN) : carbonSink)
                            .setIcon(carbonSink.compareTo(t) >= 0 ? "tCO2" : "kgC02")
            )));
            list.add(resp);
        }
        return list;
    }

    private List<EdificeUsageResp> queryEnergyAnalysisFloorUsage(List<SceneEdificeEntity> entities, LocalDateTime startTime, LocalDateTime endTime) {
        List<EdificeUsageResp> list = new ArrayList<>(entities.size());
        List<EnergyConsumptionTypeEntity> typeEntities = energyConsumptionTypeService.list(new LambdaQueryWrapper<EnergyConsumptionTypeEntity>()
                .eq(EnergyConsumptionTypeEntity::getStatus, "1")
                .orderByAsc(EnergyConsumptionTypeEntity::getCreateTm));

        List<String> edificeIds = entities.stream().map(BaseEntity::getId).collect(Collectors.toList());
        List<EnergyDataEntity> energyDataEntities = energyDataService.list(new QueryWrapper<EnergyDataEntity>()
                .select("scene_edifice_id", "energy_type_id", "SUM(standard_coal) AS standard_coal")
                .in("scene_edifice_id", edificeIds)
                .ge(Objects.nonNull(startTime), "solidify_date", startTime)
                .le(Objects.nonNull(endTime), "solidify_date", endTime)
                .groupBy("scene_edifice_id", "scene_edifice_id")
        );
        BigDecimal t = new BigDecimal("1000");

        for (SceneEdificeEntity edificeEntity : entities) {
            EdificeUsageResp resp = new EdificeUsageResp();
            resp.setName(edificeEntity.getEdificeName());
            resp.setEdificeId(edificeEntity.getId());
            resp.setPosX(edificeEntity.getPosX());
            resp.setPosY(edificeEntity.getPosY());
            resp.setPopPosition(edificeEntity.getPopPosition());
            List<EdificeUsageResp.EdificeUsageData> dataList = new ArrayList<>();

            for (EnergyConsumptionTypeEntity consumptionType : typeEntities) {
                BigDecimal value = energyDataEntities.stream()
                        .filter(item -> StringUtils.pathEquals(edificeEntity.getId(), item.getSceneEdificeId()) && StringUtils.pathEquals(item.getEnergyTypeId(), consumptionType.getId()))
                        .map(EnergyDataEntity::getStandardCoal)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN);
                boolean ge1000 = value.compareTo(t) >= 0;
                dataList.add(new EdificeUsageResp.EdificeUsageData()
                        .setTitle("用" + consumptionType.getEnergyName() + "量")
                        .setValue(ge1000 ? value.divide(t, 2, RoundingMode.HALF_DOWN) : value)
                        .setIcon(ge1000 ? "tce" : "kgce"));
            }
            resp.setData(dataList);
            list.add(resp);
        }
        return list;
    }

    private List<EdificeUsageResp> queryOverviewFloorUsage(List<SceneEdificeEntity> entities, LocalDateTime startTime, LocalDateTime endTime) {
        List<String> edificeIds = entities.stream().map(BaseEntity::getId).collect(Collectors.toList());
        // 能源使用总量
        List<EnergyDataEntity> energyDataTotalEntities = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer -> consumer
                        .add("ecp_tt_energy_data.scene_edifice_id", "sceneEdificeId", false)
                        .add("SUM(ecp_tt_energy_data.standard_coal)", "standardCoal", false)
                )
                .in(EnergyDataEntity::getSceneEdificeId, edificeIds)
                .ge(Objects.nonNull(startTime), EnergyDataEntity::getSolidifyDate, startTime)
                .le(Objects.nonNull(endTime), EnergyDataEntity::getSolidifyDate, endTime)
                .groupBy(EnergyDataEntity::getSceneEdificeId)
                .joinList(EnergyDataEntity.class);
        // 碳排放
        List<EnergyDataEntity> dataEntities = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer -> consumer
                        .add("ecp_tt_energy_data.scene_edifice_id", "sceneEdificeId", false)
                        .add("SUM(ecp_tt_energy_data.carbon_emission)", "carbonEmission", false)
                        .add("SUM(ecp_tt_energy_data.standard_coal)", "standardCoal", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .in(EnergyDataEntity::getSceneEdificeId, edificeIds)
                .ge(Objects.nonNull(startTime), EnergyDataEntity::getSolidifyDate, startTime)
                .le(Objects.nonNull(endTime), EnergyDataEntity::getSolidifyDate, endTime)
                .groupBy(EnergyDataEntity::getSceneEdificeId)
                .joinList(EnergyDataEntity.class);

        // 碳减
        List<EnergyDataEntity> reductionData = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer -> consumer
                        .add("ecp_tt_energy_data.scene_edifice_id", "sceneEdificeId", false)
                        .add("SUM(ecp_tt_energy_data.carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .in(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .in(EnergyDataEntity::getSceneEdificeId, edificeIds)
                .ge(Objects.nonNull(startTime), EnergyDataEntity::getSolidifyDate, startTime)
                .le(Objects.nonNull(endTime), EnergyDataEntity::getSolidifyDate, endTime)
                .groupBy(EnergyDataEntity::getSceneEdificeId)
                .joinList(EnergyDataEntity.class);

        List<EdificeUsageResp> list = new ArrayList<>(entities.size());
        BigDecimal t = new BigDecimal("1000");
        for (SceneEdificeEntity edificeEntity : entities) {
            EdificeUsageResp resp = new EdificeUsageResp();
            resp.setName(edificeEntity.getEdificeName());
            resp.setEdificeId(edificeEntity.getId());
            resp.setPosX(edificeEntity.getPosX());
            resp.setPosY(edificeEntity.getPosY());
            resp.setPopPosition(edificeEntity.getPopPosition());
            BigDecimal carbonEmission = dataEntities.stream()
                    .filter(item -> StringUtils.pathEquals(item.getSceneEdificeId(), edificeEntity.getId()))
                    .map(EnergyDataEntity::getCarbonEmission)
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN);

            BigDecimal standardCoal = energyDataTotalEntities.stream()
                    .filter(item -> StringUtils.pathEquals(item.getSceneEdificeId(), edificeEntity.getId()))
                    .map(EnergyDataEntity::getStandardCoal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN);

            BigDecimal carbonForest = carbonEmission.divide(edificeEntity.getFloorArea(), 2, RoundingMode.HALF_DOWN);
            BigDecimal reduction = reductionData.stream()
                    .filter(item -> StringUtils.pathEquals(item.getSceneEdificeId(), edificeEntity.getId()))
                    .map(EnergyDataEntity::getCarbonEmission)
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN);
            resp.setData(new ArrayList<>(Arrays.asList(
                    new EdificeUsageResp.EdificeUsageData().setTitle("能源使用总量")
                            .setValue(standardCoal.compareTo(t) >= 0 ? standardCoal.divide(t, 2, RoundingMode.HALF_DOWN) : standardCoal)
                            .setIcon(standardCoal.compareTo(t) >= 0 ? "tce" : "kgce"),
                    new EdificeUsageResp.EdificeUsageData().setTitle("碳排放总量")
                            .setValue(carbonEmission.compareTo(t) >= 0 ? carbonEmission.divide(t, 2, RoundingMode.HALF_DOWN) : carbonEmission)
                            .setIcon(carbonEmission.compareTo(t) >= 0 ? "tCO2" : "kgCO2"),
                    new EdificeUsageResp.EdificeUsageData().setTitle("碳排放强度")
                            .setValue(carbonForest.compareTo(t) >= 0 ? carbonForest.divide(t, 2, RoundingMode.HALF_DOWN) : carbonForest)
                            .setIcon(carbonForest.compareTo(t) >= 0 ? "tCO2/m²" : "kgCO2/m²"),
                    new EdificeUsageResp.EdificeUsageData().setTitle("碳减排总量")
                            .setValue(reduction.compareTo(t) >= 0 ? reduction.divide(t, 2, RoundingMode.HALF_DOWN) : reduction)
                            .setIcon(reduction.compareTo(t) >= 0 ? "tCO2" : "kgCO2")
            )));
            list.add(resp);
        }
        return list;
    }

    /**
     * 园区介绍
     *
     * @param id 项目Id
     */
    @ApiOperation("园区介绍")
    @GetMapping("introduction")
    public ResultResp<Map<String, Object>> introduction(@RequestParam String id) {
        SceneProjectEntity projectEntity = sceneProjectService.getById(id);
        Map<String, Object> resultData = new HashMap<>();
        String textValue = null;
        String proImgs = null;
        String backgroundImg = null;
        if (Objects.nonNull(projectEntity)) {
            textValue = projectEntity.getProRemark();
            backgroundImg = projectEntity.getBackgroundImg();
        }
        resultData.put("textValue", textValue);
        resultData.put("proImgs", proImgs);
        resultData.put("backgroundImg", backgroundImg);
        return ResultResp.success(resultData);
    }

    /**
     * 数据总览
     *
     * @param sceneProjectId projectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param startTime      开始时间
     * @param endTime        结束时间
     */
    @ApiOperation("数据总览")
    @GetMapping("dataoverview")
    public ResultResp<List<DataOverviewResp>> dataOverview(@RequestParam(required = false) String sceneProjectId,
                                                           @RequestParam(required = false) String sceneEdificeId,
                                                           @RequestParam(required = false) String sceneFloorId,
                                                           @RequestParam(required = false) String startTime,
                                                           @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        LocalDateTime preStart = Objects.isNull(start) ? LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).minusYears(1) : start.minusYears(1);
        LocalDateTime preEnd = end.minusYears(1);
        List<DataOverviewResp> result = new ArrayList<>();
        BigDecimal t = new BigDecimal("1000");
        BigDecimal pre = staticsEnergyDataOverview(sceneProjectId, sceneEdificeId, sceneFloorId, null, preStart, preEnd);
        BigDecimal value = staticsEnergyDataOverview(sceneProjectId, sceneEdificeId, sceneFloorId, null, start, end);
        String unit = pre.compareTo(t) >= 0 || value.compareTo(t) >= 0 ? "tce" : "kgce";
        if ("tce".equals(unit)) {
            pre = pre.divide(t, 2, RoundingMode.HALF_UP);
            value = value.divide(t, 2, RoundingMode.HALF_UP);
        }
        result.add(new DataOverviewResp()
                .setName("能源使用总量")
                .setUnit(unit)
                .setPreValue(pre)
                .setValue(value)); // 能源使用总量

        pre = staticsCarbonEmissionOverview(sceneProjectId, sceneEdificeId, sceneFloorId, preStart, preEnd);
        value = staticsCarbonEmissionOverview(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);
        unit = pre.compareTo(t) >= 0 || value.compareTo(t) >= 0 ? "tCO2" : "kgCO2";
        if ("tCO2".equals(unit)) {
            pre = pre.divide(t, 2, RoundingMode.HALF_UP);
            value = value.divide(t, 2, RoundingMode.HALF_UP);
        }
        result.add(new DataOverviewResp()  // 碳排放总量
                .setName("碳排放总量")
                .setUnit(unit)
                .setPreValue(pre)
                .setValue(value));

        pre = staticsPgTotalEnergyDataOverview(sceneProjectId, sceneEdificeId, sceneFloorId, preStart, preEnd);
        value = staticsPgTotalEnergyDataOverview(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);
        unit = pre.compareTo(t) >= 0 || value.compareTo(t) >= 0 ? "MWh" : "kWh";
        if ("MWh".equals(unit)) {
            pre = pre.divide(t, 2, RoundingMode.HALF_UP);
            value = value.divide(t, 2, RoundingMode.HALF_UP);
        }
        result.add(new DataOverviewResp()  // 光伏发电总量
                .setName("光伏发电总量")
                .setUnit(unit)
                .setPreValue(pre)
                .setValue(value));

        pre = staticsReductionOverview(sceneProjectId, sceneEdificeId, sceneFloorId, preStart, preEnd);
        value = staticsReductionOverview(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);
        unit = pre.compareTo(t) >= 0 || value.compareTo(t) >= 0 ? "tCO2" : "kgCO2";
        if ("tCO2".equals(unit)) {
            pre = pre.divide(t, 2, RoundingMode.HALF_UP);
            value = value.divide(t, 2, RoundingMode.HALF_UP);
        }
        result.add(new DataOverviewResp() // 碳减排总量
                .setName("碳减排总量")
                .setUnit(unit)
                .setPreValue(pre)
                .setValue(value)
                .setEquivalentTreePlanting(value.divide(new BigDecimal("20.2"), 2, RoundingMode.HALF_DOWN).intValue()));
        return ResultResp.success(result);
    }

    private BigDecimal staticsPgTotalEnergyDataOverview(String sceneProjectId,
                                                        String sceneEdificeId,
                                                        String sceneFloorId, LocalDateTime startTime, LocalDateTime endTime) {
        EnergyDataEntity dataEntity = energyDataService.getOne(new QueryWrapper<EnergyDataEntity>()
                .select("SUM(meter_value) AS meter_value")
                .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                .eq("read_meter_type", IMeterDeviceEnums.ReadMeterType.readMeterType11.readMeterType)
                .eq("statics_type", MeterDeviceStaticsTypeEnums.POWER_GENERATION.getValue())
                .ge(Objects.nonNull(startTime), "solidify_date", startTime)
                .le(Objects.nonNull(endTime), "solidify_date", endTime));
        return Objects.isNull(dataEntity) ? BigDecimal.ZERO : ObjectUtil.defaultIfNull(dataEntity.getMeterValue(), BigDecimal.ZERO)
                .setScale(2, RoundingMode.HALF_DOWN);
    }

    private BigDecimal staticsReductionOverview(String sceneProjectId,
                                                String sceneEdificeId,
                                                String sceneFloorId, LocalDateTime startTime, LocalDateTime endTime) {
        BigDecimal data = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer -> consumer
                        .add("SUM(carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .in(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(startTime), EnergyDataEntity::getSolidifyDate, startTime)
                .le(Objects.nonNull(endTime), EnergyDataEntity::getSolidifyDate, endTime)
                .joinGetOne(BigDecimal.class);
        return ObjectUtil.defaultIfNull(data, BigDecimal.ZERO).setScale(2, RoundingMode.HALF_DOWN);
    }

    private BigDecimal staticsCarbonEmissionOverview(String sceneProjectId,
                                                     String sceneEdificeId,
                                                     String sceneFloorId, LocalDateTime startTime, LocalDateTime endTime) {
        BigDecimal data = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer -> consumer
                        .add("SUM(carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(startTime), EnergyDataEntity::getSolidifyDate, startTime)
                .le(Objects.nonNull(endTime), EnergyDataEntity::getSolidifyDate, endTime)
                .joinGetOne(BigDecimal.class);
        return ObjectUtil.defaultIfNull(data, BigDecimal.ZERO).setScale(2, RoundingMode.HALF_DOWN);
    }

    private BigDecimal staticsEnergyDataOverview(String sceneProjectId,
                                                 String sceneEdificeId,
                                                 String sceneFloorId, Set<String> energyTypeIds, LocalDateTime startTime, LocalDateTime endTime) {
        EnergyDataEntity dataEntity = energyDataService.getOne(new QueryWrapper<EnergyDataEntity>()
                .select("SUM(standard_coal) AS standard_coal")
                .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                .in(!CollectionUtils.isEmpty(energyTypeIds), "energy_type_id", energyTypeIds)
                .ge(Objects.nonNull(startTime), "solidify_date", startTime)
                .le(Objects.nonNull(endTime), "solidify_date", endTime));
        return Objects.isNull(dataEntity) ? BigDecimal.ZERO : ObjectUtil.defaultIfNull(dataEntity.getStandardCoal(), BigDecimal.ZERO)
                .setScale(2, RoundingMode.HALF_DOWN);
    }

    /**
     * 能源分项统计
     *
     * @param sceneProjectId projectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param type           0：能源；1：碳排放
     * @param startTime      开始时间
     * @param endTime        结束时间
     */
    @ApiOperation("能源分项统计")
    @GetMapping("energysubstatistics")
    public ResultResp<EnergySubStatisticsResp> energySubStatistics(@RequestParam(required = false) String sceneProjectId,
                                                                   @RequestParam(required = false) String sceneEdificeId,
                                                                   @RequestParam(required = false) String sceneFloorId,
                                                                   @RequestParam String type,
                                                                   @RequestParam(required = false) String startTime,
                                                                   @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        EnergySubStatisticsResp resp = new EnergySubStatisticsResp();
        String unit = "kgce";
        BigDecimal t = new BigDecimal("1000");
        List<EnergySubStatisticsResp.Detail> details = new ArrayList<>();
        if ("0".equals(type)) {
            EnergyCategoryEntity primaryEnergy = energyCategoryService.primaryEnergy();
            List<EnergyConsumptionTypeEntity> energyConsumptionTypeEntities = energyConsumptionTypeService.findByEnergyCategoryId(primaryEnergy.getId());
            Set<String> energyTypeIds = energyConsumptionTypeEntities.stream().map(EnergyConsumptionTypeEntity::getId).collect(Collectors.toSet());
            BigDecimal onceEnergy = energyTypeIds.isEmpty() ? BigDecimal.ZERO : staticsEnergyDataOverview(sceneProjectId, sceneEdificeId, sceneFloorId, energyTypeIds, start, end);

            BigDecimal val = getDataFillingData(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);

            String electricId = energyConsumptionTypeService.getElectricId();
            List<EnergyDataEntity> energyDataEntities = Joins.of(EnergyDataEntity.class)
                    .select(EnergyDataEntity::getCarbonEmission)
                    .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                    .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                    .end()
                    .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                    .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                    .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                    .eq(EnergyDataEntity::getEnergyTypeId, electricId)
                    .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                    .le(EnergyDataEntity::getSolidifyDate, end)
                    .orderByAsc(EnergyDataEntity::getSolidifyDate)
                    .joinList(EnergyDataEntity.class);
            BigDecimal electricValue = energyDataEntities.stream()
                    .map(EnergyDataEntity::getCarbonEmission).filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN);
            BigDecimal photovoltaicPower = staticsPgTotalEnergyDataOverview(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);

            EnergyCategoryEntity othersEnergy = energyCategoryService.othersEnergy();
            energyConsumptionTypeEntities = energyConsumptionTypeService.findByEnergyCategoryId(othersEnergy.getId());
            energyTypeIds = energyConsumptionTypeEntities.stream().map(EnergyConsumptionTypeEntity::getId).collect(Collectors.toSet());
            BigDecimal otherEnergy = energyTypeIds.isEmpty() ? BigDecimal.ZERO : staticsEnergyDataOverview(sceneProjectId, sceneEdificeId, sceneFloorId, energyTypeIds, start, end);
            details.add(new EnergySubStatisticsResp.Detail().setName("一次能源").setValue(onceEnergy));
            details.add(new EnergySubStatisticsResp.Detail().setName("市电").setValue(electricValue));
            details.add(new EnergySubStatisticsResp.Detail().setName("光伏发电").setValue(photovoltaicPower));
            details.add(new EnergySubStatisticsResp.Detail().setName("外购绿电").setValue(val));
            details.add(new EnergySubStatisticsResp.Detail().setName("其他").setValue(otherEnergy));
        } else if ("1".equals(type)) {
            List<CarbonEmissionRangeEntity> rangeEntities = carbonEmissionRangeService.lambdaQuery()
                    .orderByAsc(CarbonEmissionRangeEntity::getRangeName)
                    .list();
            for (CarbonEmissionRangeEntity item : rangeEntities) {
                BigDecimal value = Joins.of(EnergyDataEntity.class)
                        .selectAs(consumer -> consumer.add("SUM(carbon_emission)", "carbonEmission", false))
                        .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                        .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                        .end()
                        .eq(EnergyDataEntity::getCalculationRange, item.getId())
                        .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                        .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                        .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                        .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                        .le(EnergyDataEntity::getSolidifyDate, end)
                        .joinGetOne(BigDecimal.class);
                details.add(new EnergySubStatisticsResp.Detail()
                        .setName(item.getRangeName())
                        .setValue(ObjectUtil.defaultIfNull(value, BigDecimal.ZERO).setScale(2, RoundingMode.HALF_DOWN)));
            }
        }

        if (details.stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tce";
            for (EnergySubStatisticsResp.Detail item : details) {
                item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_UP));
            }
        }
        resp.setData(details);
        resp.setUnit(unit);
        return ResultResp.success(resp);
    }

    private BigDecimal getDataFillingData(String sceneProjectId, String sceneEdificeId, String sceneFloorId, LocalDateTime start, LocalDateTime end) {
        BigDecimal value = Joins.of(DataFillingDataEntity.class)
                .selectAs(builder -> builder.add("SUM(carbon_emission)", "carbonEmission", false))
                .eq(StringUtils.hasLength(sceneProjectId), DataFillingDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), DataFillingDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), DataFillingDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(start), DataFillingDataEntity::getSolidifyDate, start)
                .lt(Objects.nonNull(end), DataFillingDataEntity::getSolidifyDate, end)
                .joinGetOne(BigDecimal.class);
        return ObjectUtil.defaultIfNull(value, BigDecimal.ZERO).setScale(2, RoundingMode.HALF_DOWN);
    }


    /**
     * 能源分类统计
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param type           type 0：能源；1：碳排放
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("能源分类统计")
    @GetMapping("energycategorystatistics")
    public ResultResp<List<EnergyCategoryStatisticsResp>> energyCategoryStatistics(@RequestParam(required = false) String sceneProjectId,
                                                                                   @RequestParam(required = false) String sceneEdificeId,
                                                                                   @RequestParam(required = false) String sceneFloorId,
                                                                                   @RequestParam(required = false) String sceneCompanyId,
                                                                                   @RequestParam String type,
                                                                                   @RequestParam(required = false) String startTime,
                                                                                   @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<ProductTypeEntity> productTypeEntities = productTypeService.lambdaQuery()
                .eq(ProductTypeEntity::getActiveStatus, ProductTypeActiveStatus.ENABLED.getValue())
                .orderByAsc(ProductTypeEntity::getSort)
                .list();
        List<EnergyCategoryStatisticsResp> list = new ArrayList<>(productTypeEntities.size());
        List<EnergyDataEntity> energyDataEntities = Collections.emptyList();
        if ("0".equals(type)) {
            if (!productTypeEntities.isEmpty()) {
                energyDataEntities = energyDataService.list(new QueryWrapper<EnergyDataEntity>()
                        .select("product_type_id", "SUM(standard_coal) AS standardCoal")
                        .isNotNull("product_type_id")
                        .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                        .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                        .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                        .eq(StringUtils.hasLength(sceneCompanyId), "scene_company_id", sceneCompanyId)
                        .ge(Objects.nonNull(start), "solidify_date", start)
                        .le("solidify_date", end)
                        .groupBy("product_type_id")
                );

            }
        } else if ("1".equals(type)) {
            energyDataEntities = Joins.of(EnergyDataEntity.class)
                    .selectAs(consumer -> consumer
                            .add("ecp_tt_energy_data.product_type_id", "product_type_id", false)
                            .add("SUM(ecp_tt_energy_data.standard_coal)", "standard_coal", false)
                    )
                    .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                    .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                    .end()
                    .isNotNull(EnergyDataEntity::getProductTypeId)
                    .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                    .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                    .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                    .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                    .le(EnergyDataEntity::getSolidifyDate, end)
                    .groupBy(EnergyDataEntity::getProductTypeId)
                    .joinList(EnergyDataEntity.class);
        }
        String unit = "kgce";
        for (ProductTypeEntity productType : productTypeEntities) {
            EnergyCategoryStatisticsResp statisticsResp = new EnergyCategoryStatisticsResp();
            statisticsResp.setLabel(productType.getProductTypeName());
            statisticsResp.setUnit(unit);
            BigDecimal value = energyDataEntities.stream()
                    .filter(item -> org.apache.commons.lang3.StringUtils.equals(item.getProductTypeId(), productType.getId()))
                    .map(EnergyDataEntity::getStandardCoal).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            statisticsResp.setValue(ObjectUtil.defaultIfNull(value, BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP));
            list.add(statisticsResp);
        }
        BigDecimal t = new BigDecimal("1000");
        if (list.stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tce";
            for (EnergyCategoryStatisticsResp item : list) {
                item.setUnit(unit);
                item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_UP));
            }
        }
        return ResultResp.success(list);
    }

    /**
     * 预测精度 下拉数据： 发电量， 用电量
     */
    @ApiOperation("预测精度 下拉数据： 发电量， 用电量")
    @GetMapping("predictionaccuracy")
    public ResultResp<List<SelectedResp>> predictionAccuracy() {
        List<SelectedResp> selectedResps = new ArrayList<>();
        selectedResps.add(SelectedResp.builder()
                .label(MeterDeviceStaticsTypeEnums.POWER_GENERATION.getLabel())
                .value(MeterDeviceStaticsTypeEnums.POWER_GENERATION.getValue())
                .build());
        selectedResps.add(SelectedResp.builder()
                .label(MeterDeviceStaticsTypeEnums.POWER_CONSUMPTION.getLabel())
                .value(MeterDeviceStaticsTypeEnums.POWER_CONSUMPTION.getValue())
                .build());
        return ResultResp.success(selectedResps);
    }

    /**
     * 碳排放量趋势
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param type           type 0：时；1：日
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("碳排放量趋势")
    @GetMapping("carbonEmissionsTrend")
    public ResultResp<List<CarbonEmissionsTrendResp>> carbonEmissionsTrend(@RequestParam(required = false) String sceneProjectId,
                                                                           @RequestParam(required = false) String sceneEdificeId,
                                                                           @RequestParam(required = false) String sceneFloorId,
                                                                           @RequestParam String type,
                                                                           @RequestParam(required = false) String startTime,
                                                                           @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<EnergyDataEntity> energyDataEntities = Joins.of(EnergyDataEntity.class)
                .select(EnergyDataEntity::getSolidifyDate, EnergyDataEntity::getCarbonEmission)
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end)
                .orderByAsc(EnergyDataEntity::getSolidifyDate)
                .joinList(EnergyDataEntity.class);
        List<CarbonEmissionsTrendResp> trends = new ArrayList<>();
        String dateFormat = "yyyy-MM-dd HH:00";
        if ("1".equals(type)) {
            dateFormat = "yyyy-MM-dd";
        }
        String finalFormat = dateFormat;
        Map<String, List<EnergyDataEntity>> groupBySolidifyDateMap = energyDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), finalFormat), TreeMap::new, Collectors.toList()));
        List<CarbonEmissionsTrendResp.DataDetail> dataDetailList = new ArrayList<>();
        for (Map.Entry<String, List<EnergyDataEntity>> entry : groupBySolidifyDateMap.entrySet()) {
            dataDetailList.add(new CarbonEmissionsTrendResp.DataDetail()
                    .setTime(entry.getKey())
                    .setValue(entry.getValue()
                            .stream()
                            .map(EnergyDataEntity::getCarbonEmission)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN)));
        }
        String unit = "kgCO2";
        BigDecimal t = new BigDecimal("1000");
        if (dataDetailList.stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tCO2";
            for (CarbonEmissionsTrendResp.DataDetail item : dataDetailList) {
                item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
            }
        }
        trends.add(new CarbonEmissionsTrendResp().setName("碳排放量")
                .setUnit(unit)
                .setData(dataDetailList));
        return ResultResp.success(trends);
    }

    /**
     * 预测精度数据
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param type           type 1：发电量；5：用电量
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("预测精度数据")
    @GetMapping("predictionaccuracydata")
    public ResultResp<PredictionAccuracyData> predictionAccuracyData(@RequestParam(required = false) String sceneProjectId,
                                                                     @RequestParam(required = false) String sceneCompanyId,
                                                                     @RequestParam(required = false) String sceneEdificeId,
                                                                     @RequestParam(required = false) String sceneFloorId,
                                                                     @RequestParam String type,
                                                                     @RequestParam(required = false) String startTime,
                                                                     @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();

        PredictionAccuracyData result = new PredictionAccuracyData();
        List<String> staticTypes = Arrays.asList(MeterDeviceStaticsTypeEnums.POWER_GENERATION.getValue(), MeterDeviceStaticsTypeEnums.POWER_CONSUMPTION.getValue());
        result.setTotalPredictionAccuracy(energyForecastDataService.sumForecastPrecision(sceneProjectId, sceneCompanyId,
                sceneEdificeId, sceneFloorId, null, staticTypes, start, end)); // 管理后台-数据管理-能源预测数据-预测精度相加（统计类型为：发电量/用电量）
        List<PredictionAccuracyData.PredictionAccuracy> accuracies = new ArrayList<>();
        List<EnergyDataEntity> dataEntities = energyDataService.list(new QueryWrapper<EnergyDataEntity>()
                .select("solidify_date", " meter_value")
                .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                .eq(StringUtils.hasLength(sceneCompanyId), "scene_company_id", sceneCompanyId)
                .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                .eq("statics_type", type)
                .ge(Objects.nonNull(start), "solidify_date", start)
                .le("solidify_date", end).orderByAsc("solidify_date"));
        if (MeterDeviceStaticsTypeEnums.POWER_GENERATION.getValue().equals(type)) {
            accuracies.add(new PredictionAccuracyData.PredictionAccuracy().setName("预测值")
                    .setData(energyForecastDataService.queryForList(sceneProjectId, sceneCompanyId,
                                    sceneEdificeId, sceneFloorId, null, staticTypes, start, end)
                            .stream().map(item -> new PredictionAccuracyData.DataDetail().setTime(item.getCreateTm()).setValue(item.getMeterForecastValue()))
                            .collect(Collectors.toList()))); // 能源计量值数据来源：管理后台-数据管理-能源预测数据-计量值（统计类型为：发电量/用电量）
            accuracies.add(new PredictionAccuracyData.PredictionAccuracy()
                    .setName("实际值").setData(dataEntities.stream()
                            .map(item -> new PredictionAccuracyData.DataDetail().setTime(item.getSolidifyDate())
                                    .setValue(item.getMeterValue().setScale(2, RoundingMode.HALF_DOWN)))
                            .collect(Collectors.toList())));
        } else if (MeterDeviceStaticsTypeEnums.POWER_CONSUMPTION.getValue().equals(type)) {
            accuracies.add(new PredictionAccuracyData.PredictionAccuracy().setName("预测值").setData(
                    energyForecastDataService.queryForList(sceneProjectId, sceneCompanyId,
                                    sceneEdificeId, sceneFloorId, null, staticTypes, start, end)
                            .stream().map(item -> new PredictionAccuracyData.DataDetail().setTime(item.getCreateTm()).setValue(item.getMeterForecastValue()))
                            .collect(Collectors.toList()))); // 能源计量值数据来源：管理后台-数据管理-能源预测数据-计量值（统计类型为：发电量/用电量）
            accuracies.add(new PredictionAccuracyData.PredictionAccuracy()
                    .setName("实际值")
                    .setData(dataEntities.stream()
                            .map(item -> new PredictionAccuracyData.DataDetail().setTime(item.getSolidifyDate())
                                    .setValue(item.getMeterValue().setScale(2, RoundingMode.HALF_DOWN)))
                            .collect(Collectors.toList())));
        }
        result.setData(accuracies);
        return ResultResp.success(result);
    }


    /**
     * 减排量、碳汇趋势
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param type           type 0：时；1：日
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("减排量、碳汇趋势")
    @GetMapping("reductioncarbonsinktrends")
    public ResultResp<List<ReductionCarbonSinkTrends>> reductionCarbonSinkTrends(@RequestParam(required = false) String sceneProjectId,
                                                                                 @RequestParam(required = false) String sceneCompanyId,
                                                                                 @RequestParam(required = false) String sceneEdificeId,
                                                                                 @RequestParam(required = false) String sceneFloorId,
                                                                                 @RequestParam String type,
                                                                                 @RequestParam(required = false) String startTime,
                                                                                 @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<EnergyDataEntity> energyDataEntities = Joins.of(EnergyDataEntity.class)
                .select(EnergyDataEntity::getSolidifyDate, EnergyDataEntity::getCarbonEmission)
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .in(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneCompanyId), EnergyDataEntity::getSceneCompanyId, sceneCompanyId)
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end)
                .orderByAsc(EnergyDataEntity::getSolidifyDate)
                .joinList(EnergyDataEntity.class);
        List<ReductionCarbonSinkTrends> trends = new ArrayList<>();
        String dateFormat = "yyyy-MM-dd HH:00";
        if ("1".equals(type)) {
            dateFormat = "yyyy-MM-dd";
        }
        String unit = "kgCO2";
        String finalFormat = dateFormat;
        Map<String, List<EnergyDataEntity>> groupBySolidifyDateMap = energyDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), finalFormat), TreeMap::new, Collectors.toList()));
        List<ReductionCarbonSinkTrends.DataDetail> dataDetailList = new ArrayList<>();
        for (Map.Entry<String, List<EnergyDataEntity>> entry : groupBySolidifyDateMap.entrySet()) {
            dataDetailList.add(new ReductionCarbonSinkTrends.DataDetail().setTime(entry.getKey())
                    .setValue(entry.getValue()
                            .stream()
                            .map(EnergyDataEntity::getCarbonEmission)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .setScale(2, RoundingMode.HALF_UP)));
        }
        trends.add(new ReductionCarbonSinkTrends().setName("减排量").setUnit(unit).setData(dataDetailList));

        List<CarbonSinkDataEntity> carbonSinkDataEntities = carbonSinkDataService.list(new LambdaQueryWrapper<CarbonSinkDataEntity>()
                .eq(StringUtils.hasLength(sceneProjectId), CarbonSinkDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), CarbonSinkDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), CarbonSinkDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(start), CarbonSinkDataEntity::getSolidifyDate, start)
                .le(CarbonSinkDataEntity::getSolidifyDate, end)
                .orderByAsc(CarbonSinkDataEntity::getSolidifyDate));
        Map<String, List<CarbonSinkDataEntity>> groupBySolidifyDateMap2 = carbonSinkDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), finalFormat), TreeMap::new, Collectors.toList()));
        dataDetailList = new ArrayList<>();
        for (Map.Entry<String, List<CarbonSinkDataEntity>> entry : groupBySolidifyDateMap2.entrySet()) {
            dataDetailList.add(new ReductionCarbonSinkTrends.DataDetail().setTime(entry.getKey()).setValue(entry.getValue()
                    .stream()
                    .map(CarbonSinkDataEntity::getCarbonEmission)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .setScale(2, RoundingMode.HALF_UP)));
        }
        trends.add(new ReductionCarbonSinkTrends().setName("碳汇").setUnit(unit).setData(dataDetailList));

        BigDecimal t = new BigDecimal("1000");
        if (trends.stream()
                .anyMatch(item -> item.getData()
                        .stream()
                        .anyMatch(dataItem -> dataItem.getValue().compareTo(t) >= 0))) {
            unit = "tCO2";
            for (ReductionCarbonSinkTrends item : trends) {
                item.setUnit(unit);
                for (ReductionCarbonSinkTrends.DataDetail dateItem : item.getData()) {
                    dateItem.setValue(dateItem.getValue().divide(t, 2, RoundingMode.HALF_UP));
                }
            }
        }
        return ResultResp.success(trends);
    }

    /**
     * 碳资产统计
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("碳资产统计")
    @GetMapping("carbonassetstatistics")
    public ResultResp<CarbonAssetStatisticsResp> carbonAssetStatistics(@RequestParam(required = false) String sceneProjectId,
                                                                       @RequestParam(required = false) String sceneEdificeId,
                                                                       @RequestParam(required = false) String sceneFloorId,
                                                                       @RequestParam(required = false) String startTime,
                                                                       @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        CarbonQuotaStaticsResp staticsCarbonQuota = staticsCarbonQuota(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);
        CarbonCCERStaticsResp staticsCCER = staticsCCER(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);
        CarbonAssetStatisticsResp statisticsResp = new CarbonAssetStatisticsResp();
        statisticsResp.setTotal(staticsCarbonQuota.getRemainingValue().add(staticsCCER.getRemainingValue()));
        statisticsResp.setTotalSel(staticsCCER.getTotalValue());
        statisticsResp.setTotalEnd(staticsCCER.getSaleValue());
        return ResultResp.success(statisticsResp);
    }

    private CarbonCCERStaticsResp staticsCCER(String sceneProjectId,
                                              String sceneEdificeId,
                                              String sceneFloorId, LocalDateTime startTime,
                                              LocalDateTime endTime) {
        CarbonCCERStaticsResp staticsResp = new CarbonCCERStaticsResp();
        List<CarbonCCEREntity> carbonCCEREntities = carbonCCERService.list(new LambdaQueryWrapper<CarbonCCEREntity>()
                .eq(StringUtils.hasLength(sceneProjectId), CarbonCCEREntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), CarbonCCEREntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), CarbonCCEREntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(startTime), CarbonCCEREntity::getCreateTm, startTime)
                .le(CarbonCCEREntity::getCreateTm, endTime)
        );
        Map<String, List<CarbonCCEREntity>> inputTypeMap = carbonCCEREntities.stream().collect(Collectors.groupingBy(CarbonCCEREntity::getInputType));
        staticsResp.setSaleValue(inputTypeMap.getOrDefault(CarbonQuotaInputTypeEnums.SOLD.getValue(), Collections.emptyList())
                .stream().map(CarbonCCEREntity::getCcerValue).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN));
        staticsResp.setPurchasedValue(inputTypeMap.getOrDefault(CarbonQuotaInputTypeEnums.PURCHASE.getValue(), Collections.emptyList())
                .stream().map(CarbonCCEREntity::getCcerValue).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN));

        staticsResp.setTotalValue(queryReductionData(sceneProjectId, sceneEdificeId, sceneFloorId, null, startTime, endTime)
                .add(staticsResp.getPurchasedValue()).setScale(2, RoundingMode.HALF_DOWN));
        staticsResp.setRemainingValue(staticsResp.getTotalValue()
                .subtract(staticsResp.getSaleValue()));
        return staticsResp;
    }

    private CarbonQuotaStaticsResp staticsCarbonQuota(String sceneProjectId,
                                                      String sceneEdificeId,
                                                      String sceneFloorId, LocalDateTime startTime,
                                                      LocalDateTime endTime) {
        CarbonQuotaStaticsResp staticsResp = new CarbonQuotaStaticsResp();
        List<CarbonQuotaEntity> quotaEntities = carbonQuotaService.list(new LambdaQueryWrapper<CarbonQuotaEntity>()
                .eq(StringUtils.hasLength(sceneProjectId), CarbonQuotaEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), CarbonQuotaEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), CarbonQuotaEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(startTime), CarbonQuotaEntity::getCreateTm, startTime)
                .le(CarbonQuotaEntity::getCreateTm, endTime)
        );
        Map<String, List<CarbonQuotaEntity>> inputTypeMap = quotaEntities.stream().collect(Collectors.groupingBy(CarbonQuotaEntity::getInputType));
        BigDecimal totalValue = inputTypeMap.getOrDefault(CarbonQuotaInputTypeEnums.ADD.getValue(), Collections.emptyList())
                .stream().map(CarbonQuotaEntity::getQuotaValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        staticsResp.setTotalValue(totalValue);
        staticsResp.setSaleValue(inputTypeMap.getOrDefault(CarbonQuotaInputTypeEnums.SOLD.getValue(), Collections.emptyList())
                .stream().map(CarbonQuotaEntity::getQuotaValue).reduce(BigDecimal.ZERO, BigDecimal::add));
        staticsResp.setPurchasedValue(inputTypeMap.getOrDefault(CarbonQuotaInputTypeEnums.PURCHASE.getValue(), Collections.emptyList())
                .stream().map(CarbonQuotaEntity::getQuotaValue).reduce(BigDecimal.ZERO, BigDecimal::add));

        staticsResp.setUseValue(getUsedValue(sceneProjectId, sceneEdificeId,
                sceneFloorId, startTime, endTime));

        staticsResp.setRemainingValue(staticsResp.getTotalValue()
                .subtract(staticsResp.getUseValue())
                .add(staticsResp.getPurchasedValue())
                .subtract(staticsResp.getSaleValue()));
        return staticsResp;
    }

    private BigDecimal queryReductionData(String sceneProjectId,
                                          String sceneEdificeId,
                                          String sceneFloorId,
                                          String sceneRoomId,
                                          LocalDateTime startTime, LocalDateTime endTime) {
        BigDecimal data = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer -> consumer
                        .add("SUM(carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .in(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneRoomId, sceneRoomId)
                .ge(Objects.nonNull(startTime), EnergyDataEntity::getSolidifyDate, startTime)
                .le(Objects.nonNull(endTime), EnergyDataEntity::getSolidifyDate, endTime)
                .joinGetOne(BigDecimal.class);
        return ObjectUtil.defaultIfNull(data, BigDecimal.ZERO);
    }

    private BigDecimal getUsedValue(String sceneProjectId, String sceneEdificeId, String sceneFloorId, LocalDateTime start, LocalDateTime end) {
        BigDecimal res = Joins.of(EnergyDataEntity.class)
                .selectAs(builder -> builder
                        .add("SUM(carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getEnergyTypeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneEdificeId, sceneFloorId)
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(Objects.nonNull(end), EnergyDataEntity::getSolidifyDate, end)
                .joinGetOne(BigDecimal.class);
        return Objects.isNull(res) ? BigDecimal.ZERO : res.setScale(2, RoundingMode.HALF_DOWN);
    }

    /**
     * 顶部栏展示数据
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("顶部栏展示数据")
    @GetMapping("topdata")
    public ResultResp<TopDataResp> topData(@RequestParam(required = false) String sceneProjectId,
                                           @RequestParam(required = false) String sceneEdificeId,
                                           @RequestParam(required = false) String sceneFloorId,
                                           @RequestParam(required = false) String startTime,
                                           @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        BigDecimal value = getEnergyUseFlexibility(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);
        BigDecimal t = new BigDecimal("1000");
        BigDecimal powerGeneration = getPowerGeneration(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);
        BigDecimal emissionIntensityData = getEmissionIntensityData(sceneProjectId, sceneEdificeId, sceneFloorId, start, end);
        return ResultResp.success(new TopDataResp()
                .setEnergyUse(value.compareTo(t) >= 0 ? value.divide(t, 2, RoundingMode.HALF_DOWN) : value)
                .setEnergyUseUnit(value.compareTo(t) >= 0 ? "tCO2/m²" : "kgCO2/m²")
                .setPowerGeneration(powerGeneration.compareTo(t) >= 0 ? powerGeneration.divide(t, 2, RoundingMode.HALF_DOWN) : powerGeneration)
                .setPowerGenerationUnit(powerGeneration.compareTo(t) >= 0 ? "MW" : "kW")
                .setEmissionIntensity(emissionIntensityData.compareTo(t) >= 0 ? emissionIntensityData.divide(t, 2, RoundingMode.HALF_DOWN) : emissionIntensityData)
                .setEmissionIntensityUnit(emissionIntensityData.compareTo(t) >= 0 ? "tCO2/m²" : "kgCO2/m²")); // 碳排放强度： 数据来源：管理后台-数据管理-碳排强度数据-碳排强度。
    }

    /**
     * TODO : 数据来源：管理后台-设备运行状态-。  公式：可调设备柔性度=abs（调控后当前运行功率-标准运行功率） 3、能源使用柔性度：空间场景、时间条件下所有可调设备的柔性度之和
     */
    private BigDecimal getEnergyUseFlexibility(String sceneProjectId,
                                               String sceneEdificeId,
                                               String sceneFloorId,
                                               LocalDateTime start, LocalDateTime end) {
        return BigDecimal.ZERO;
    }

    /**
     * 光伏发电自用量:
     * <p>
     * 光伏发电使用量 = 光伏发电量-光伏上网电量
     */
    private BigDecimal getPowerGeneration(String sceneProjectId, String sceneEdificeId, String sceneFloorId, LocalDateTime start, LocalDateTime end) {
        // 光伏发电量
        BigDecimal powerGeneration = ObjectUtil.defaultIfNull(Joins.of(EnergyDataEntity.class)
                .selectAs(builder -> builder.add("SUM(meter_value)", "meterValue", false))
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType11.getValue())
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.POWER_GENERATION.getValue())
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(Objects.nonNull(end), EnergyDataEntity::getSolidifyDate, end)
                .joinGetOne(BigDecimal.class), BigDecimal.ZERO);
        // 光伏上网电量
        BigDecimal onGrid = ObjectUtil.defaultIfNull(Joins.of(EnergyDataEntity.class)
                .selectAs(builder -> builder.add("SUM(meter_value)", "meterValue", false))
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType11.getValue())
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.ON_GRID_ENERGY.getValue())
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(Objects.nonNull(end), EnergyDataEntity::getSolidifyDate, end)
                .joinGetOne(BigDecimal.class), BigDecimal.ZERO);
        return powerGeneration.subtract(onGrid).setScale(2, RoundingMode.HALF_DOWN);
    }

    /**
     * 碳排放强度
     */
    private BigDecimal getEmissionIntensityData(String sceneProjectId, String sceneEdificeId, String sceneFloorId, LocalDateTime start, LocalDateTime end) {
        List<EmissionIntensityDataResp> list = Joins.of(EnergyDataEntity.class)
                .select(EnergyDataEntity::getCarbonEmission)
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .selectAs(t -> t.addFunAlias(MeterDeviceEntity::getDeviceCode, EnergyDataExtEntity::getDeviceCode)
                        .addFunAlias(MeterDeviceEntity::getDeviceName, EnergyDataExtEntity::getDeviceName)
                        .addFunAlias(MeterDeviceEntity::getReadMeterType, EnergyDataExtEntity::getReadMeterType)
                        .addFunAlias(MeterDeviceEntity::getReadMeterTag, EnergyDataExtEntity::getReadMeterTag)
                )
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, EnergyDataEntity::getSceneProjectId)
                .selectAs(build ->
                        build
                                .add("ecp_tt_scene_project.project_name", "project_name", false)
                                .add("ecp_tt_scene_project.floor_area", "project_floor_area", false)
                )
                .end()
                .leftJoin(SceneEdificeEntity.class, SceneEdificeEntity::getId, EnergyDataEntity::getSceneEdificeId)
                .selectAs(SceneEdificeEntity::getFloorArea, EmissionIntensityDataResp::getEdificeFloorArea)
                .end()
                .leftJoin(SceneFloorEntity.class, SceneFloorEntity::getId, EnergyDataEntity::getSceneFloorId)
                .selectAs(SceneFloorEntity::getFloorArea, EmissionIntensityDataResp::getFloorArea)
                .end()
                .leftJoin(SceneRoomEntity.class, SceneRoomEntity::getId, EnergyDataEntity::getSceneRoomId)
                .selectAs(SceneRoomEntity::getRoomArea, EmissionIntensityDataResp::getArea)
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(Objects.nonNull(end), EnergyDataEntity::getSolidifyDate, end).joinList(EmissionIntensityDataResp.class);

        return list.stream()
                .map(item -> {
                    BigDecimal area = item.getArea();
                    if (Objects.isNull(area)) {
                        area = item.getFloorArea();
                    }
                    if (Objects.isNull(area)) {
                        area = item.getEdificeFloorArea();
                    }
                    if (Objects.isNull(area)) {
                        area = item.getProjectFloorArea();
                    }
                    return item.getCarbonEmission().divide(ObjectUtil.defaultIfNull(area, BigDecimal.ZERO), 2, RoundingMode.HALF_DOWN);
                })
                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN);
    }

    /**
     * 设备信息-总量统计
     */
    @ApiOperation("设备信息-总量统计")
    @GetMapping("deviceinfo")
    public ResultResp<DeviceInfoResp> deviceInfo(@RequestParam(required = false) String sceneProjectId,
                                                 @RequestParam(required = false) String sceneEdificeId,
                                                 @RequestParam(required = false) String sceneFloorId) {
        DeviceInfoResp infoResp = new DeviceInfoResp();
        infoResp.setOnLineDevicesNum(deviceService.countOnline(sceneProjectId, sceneEdificeId, sceneFloorId) +
                meterDeviceService.countOnline(sceneProjectId, sceneEdificeId, sceneFloorId));
        infoResp.setOutLineDevicesNum(deviceService.countOffline(sceneProjectId, sceneEdificeId, sceneFloorId) +
                meterDeviceService.countOffline(sceneProjectId, sceneEdificeId, sceneFloorId));
        infoResp.setDevicesNum(infoResp.getOnLineDevicesNum() + infoResp.getOutLineDevicesNum());
        return ResultResp.success(infoResp);
    }

    /**
     * 设备信息-设备/计量设备
     *
     * @param type 0：设备；1：计量设备
     */
    @ApiOperation("设备信息-电力设备/计量设备")
    @GetMapping("deviceinfo/category")
    public ResultResp<List<DeviceInfoCategoryResp>> deviceInfoByCategory(
            @RequestParam(required = false) String sceneProjectId,
            @RequestParam(required = false) String sceneEdificeId,
            @RequestParam(required = false) String sceneFloorId,
            @RequestParam String type,
            @RequestParam(required = false) String startTime,
            @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<DeviceInfoCategoryResp> resps = new ArrayList<>();
        List<ProductTypeEntity> productTypeEntities = productTypeService.list(new LambdaQueryWrapper<ProductTypeEntity>()
                .eq(ProductTypeEntity::getActiveStatus, ProductTypeActiveStatus.ENABLED.getValue())
                .orderByAsc(ProductTypeEntity::getSort));
        Set<String> productTypeIds = productTypeEntities.stream().map(BaseEntity::getId).collect(Collectors.toSet());
        if (!productTypeIds.isEmpty()) {
            List<Map<String, Object>> listMaps = Collections.emptyList();
            if (StringUtils.pathEquals(type, "0")) {
                listMaps = deviceService.listMaps(new QueryWrapper<DeviceEntity>()
                        .select("product_type_id", "device_status", "count(id) as count_value")
                        .eq("device_type", IDeviceEnums.DeviceType.deviceType1.getValue())
                        .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                        .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                        .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                        .in("product_type_id", productTypeIds)
                        .isNotNull("device_status")
                        .groupBy("product_type_id", "device_status")
                );

            } else if (StringUtils.pathEquals(type, "1")) {
                listMaps = meterDeviceService.listMaps(new QueryWrapper<MeterDeviceEntity>()
                        .select("product_type_id", "device_status", "count(id) as count_value")
                        .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                        .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                        .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                        .eq("del_flag", IMeterDeviceEnums.DelFlag.delFlag1.delFlag)
                        .in("product_type_id", productTypeIds)
                        .isNotNull("device_status")
                        .groupBy("product_type_id", "device_status")
                );
            }
            for (ProductTypeEntity productTypeEntity : productTypeEntities) {
                Object onlineCount = listMaps.stream()
                        .filter(item -> StringUtils.pathEquals((String) item.get("product_type_id"), productTypeEntity.getId()) && "1".equals(item.get("device_status")))
                        .map(item -> item.get("count_value"))
                        .findFirst()
                        .orElse(null);
                int online = Objects.isNull(onlineCount) ? 0 : Integer.parseInt(onlineCount.toString());
                Object offlineCount = listMaps.stream()
                        .filter(item -> StringUtils.pathEquals((String) item.get("product_type_id"), productTypeEntity.getId()) && "2".equals(item.get("device_status")))
                        .map(item -> item.get("count_value"))
                        .findFirst()
                        .orElse(null);
                int offline = Objects.isNull(offlineCount) ? 0 : Integer.parseInt(offlineCount.toString());
                DeviceInfoCategoryResp categoryResp = new DeviceInfoCategoryResp()
                        .setLabelName(productTypeEntity.getProductTypeName())
                        .setOnLineDevicesNum(online)
                        .setOutLineDevicesNum(offline);
                if ("电表".equals(productTypeEntity.getProductTypeName())) {
                    // 电表总功率数据来源：
                    //电表总功率=计量值/时间（15分钟的计量值*0.25，1小时的计量值*1）【现在数据的颗粒度为15分钟】
                    //（1）电表设备的计量值数据来源：管理后台-数据管理-能源数据-计量值（此处计量为读表类型为“用电电表”并且统计类型为“用电量”的计量值）
                    BigDecimal meterValue = energyDataService.getOneOpt(new QueryWrapper<EnergyDataEntity>()
                                    .select("meter_value")
                                    .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                                    .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                                    .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                                    .eq("product_type_id", productTypeEntity.getId())
                                    .eq("read_meter_type", IMeterDeviceEnums.ReadMeterType.readMeterType13.readMeterType)
                                    .eq("statics_type", MeterDeviceStaticsTypeEnums.POWER_CONSUMPTION.getValue())
                                    .ge(Objects.nonNull(start), "solidify_date", start)
                                    .le("solidify_date", end)
                                    .last(ParamConstants.LIMIT_ONE)
                                    .orderByDesc("solidify_date"))
                            .map(EnergyDataEntity::getMeterValue).orElse(BigDecimal.ZERO);
                    categoryResp.setTotalPower(meterValue.multiply(new BigDecimal("0.25")).setScale(2, RoundingMode.HALF_DOWN));
                }
                resps.add(categoryResp);
            }
        }

        return ResultResp.success(resps.stream().filter(item -> item.getOnLineDevicesNum() > 0 || item.getOutLineDevicesNum() > 0).collect(Collectors.toList()));
    }

    /**
     * 能源分析 图表数据
     *
     * @param sceneEdificeIds sceneEdificeIds，多个用,分隔
     * @param sceneFloorIds   sceneFloorIds 多个用,分隔
     * @param sceneRoomIds    sceneRoomIds 多个用,分隔
     * @param categoryIds     categoryIds 多个用,分隔
     */
    @ApiOperation("能源分析-图表数据")
    @GetMapping("energyanalysis")
    public ResultResp<List<EnergyAnalysisResp>> energyAnalysis(@RequestParam(required = false) String sceneEdificeIds,
                                                               @RequestParam(required = false) String sceneFloorIds,
                                                               @RequestParam(required = false) String sceneRoomIds,
                                                               @RequestParam(required = false) String categoryIds,
                                                               @RequestParam(required = false) String startTime,
                                                               @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        Set<String> sceneEdifices = Arrays.stream(StringUtils.tokenizeToStringArray(sceneEdificeIds, ",")).collect(Collectors.toSet());
        Set<String> sceneFloors = Arrays.stream(StringUtils.tokenizeToStringArray(sceneFloorIds, ",")).collect(Collectors.toSet());
        Set<String> sceneRooms = Arrays.stream(StringUtils.tokenizeToStringArray(sceneRoomIds, ",")).collect(Collectors.toSet());
        Set<String> categories = Arrays.stream(StringUtils.tokenizeToStringArray(categoryIds, ",")).collect(Collectors.toSet());
        List<ProductTypeEntity> productTypeEntities = productTypeService.list(new LambdaQueryWrapper<ProductTypeEntity>()
                .eq(ProductTypeEntity::getActiveStatus, ProductTypeActiveStatus.ENABLED.getValue())
                .orderByAsc(ProductTypeEntity::getSort));
        List<String> dynamicSelectFields = new ArrayList<>();
        dynamicSelectFields.add("product_type_id");
        dynamicSelectFields.add("scene_edifice_id");
        List<String> dynamicGroupFields = new ArrayList<>();
        dynamicGroupFields.add("product_type_id");
        dynamicGroupFields.add("scene_edifice_id");
        dynamicSelectFields.add("SUM(standard_coal) AS standardCoal");
        List<EnergyDataEntity> list = energyDataService.list(new QueryWrapper<EnergyDataEntity>()
                .select(dynamicSelectFields)
                .in(!CollectionUtils.isEmpty(sceneEdifices), "scene_edifice_id", sceneEdifices)
                .in(!CollectionUtils.isEmpty(sceneFloors), "scene_floor_id", sceneFloors)
                .in(!CollectionUtils.isEmpty(sceneRooms), "scene_room_id", sceneRooms)
                .in(!CollectionUtils.isEmpty(categories), "product_type_id", categories)
                .ge(Objects.nonNull(start), "solidify_date", start)
                .le("solidify_date", end)
                .isNotNull("scene_edifice_id")
                .isNotNull("product_type_id")
                .groupBy(dynamicGroupFields)
                .orderByAsc("solidify_date")
        );
        Set<String> edificeIds = list.stream().map(EnergyDataEntity::getSceneEdificeId).collect(Collectors.toSet());
        List<SceneEdificeEntity> sceneEdificeEntities = edificeIds.isEmpty() ? Collections.emptyList() : sceneEdificeService.listByIds(edificeIds);
        List<EnergyAnalysisResp> resultData = new ArrayList<>();
        String unit = "kgce";
        for (ProductTypeEntity productType : productTypeEntities) {
            Map<String, List<EnergyDataEntity>> edificeDataMap = list.stream().filter(item -> StringUtils.pathEquals(item.getProductTypeId(), productType.getId()))
                    .collect(Collectors.groupingBy(EnergyDataEntity::getSceneEdificeId));
            List<EnergyAnalysisResp.DataItem> dataItems = new ArrayList<>();
            for (Map.Entry<String, List<EnergyDataEntity>> entry : edificeDataMap.entrySet()) {
                EnergyAnalysisResp.DataItem dataItem = new EnergyAnalysisResp.DataItem();
                dataItem.setId(entry.getKey());
                SceneEdificeEntity sceneEdificeEntity = sceneEdificeEntities.stream()
                        .filter(item -> StringUtils.pathEquals(dataItem.getId(), item.getId()))
                        .findFirst()
                        .orElse(null);
                dataItem.setName(Objects.isNull(sceneEdificeEntity) ? null : sceneEdificeEntity.getEdificeName());
                dataItem.setValue(entry.getValue().stream()
                        .map(EnergyDataEntity::getStandardCoal)
                        .filter(Objects::nonNull)
                        .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN));
                dataItems.add(dataItem);
            }
            resultData.add(new EnergyAnalysisResp().setName(productType.getProductTypeName()).setUnit(unit).setData(dataItems));
        }
        BigDecimal t = new BigDecimal("1000");
        if (resultData.stream().anyMatch(item -> item.getData().stream().anyMatch(dataItem -> dataItem.getValue().compareTo(t) >= 0))) {
            unit = "tce";
            for (EnergyAnalysisResp resultDatum : resultData) {
                resultDatum.setUnit(unit);
                for (EnergyAnalysisResp.DataItem dataItem : resultDatum.getData()) {
                    dataItem.setValue(dataItem.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
                }
            }
        }
        return ResultResp.success(resultData);
    }

    /**
     * 能源结构
     */
    @ApiOperation("能源结构")
    @GetMapping("energystructure")
    public ResultResp<List<EnergyStructureDataResp>> energyStructure(@RequestParam(required = false) String sceneProjectId,
                                                                     @RequestParam(required = false) String sceneEdificeId,
                                                                     @RequestParam(required = false) String sceneFloorId,
                                                                     @RequestParam(required = false) String startTime,
                                                                     @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();

        List<EnergyConsumptionTypeEntity> consumptionTypeEntities = energyConsumptionTypeService.lambdaQuery()
                .eq(EnergyConsumptionTypeEntity::getStatus, IMeterDeviceEnums.DelFlag.delFlag1.delFlag)
                .orderByAsc(EnergyConsumptionTypeEntity::getCreateTm)
                .list();
        List<EnergyDataEntity> list = energyDataService.list(new QueryWrapper<EnergyDataEntity>()
                .select("energy_type_id", "SUM(standard_coal) AS standardCoal")
                .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                .ge(Objects.nonNull(start), "solidify_date", start)
                .le("solidify_date", end)
                .isNotNull("energy_type_id")
                .groupBy("energy_type_id")
                .orderByAsc("solidify_date")
        );
        List<EnergyStructureDataResp> dataList = new ArrayList<>();
        String unit = "kgce";
        for (EnergyConsumptionTypeEntity consumptionType : consumptionTypeEntities) {
            dataList.add(new EnergyStructureDataResp()
                    .setId(consumptionType.getId())
                    .setName(consumptionType.getEnergyName())
                    .setUnit(unit)
                    .setValue(list.stream()
                            .filter(item -> Objects.nonNull(item.getStandardCoal()) && StringUtils.pathEquals(consumptionType.getId(), item.getEnergyTypeId()))
                            .map(EnergyDataEntity::getStandardCoal)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .setScale(2, RoundingMode.HALF_DOWN))
            );
        }
        BigDecimal t = new BigDecimal("1000");
        if (dataList.stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tce";
            for (EnergyStructureDataResp item : dataList) {
                item.setUnit(unit);
                item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
            }
        }
        return ResultResp.success(dataList);
    }

    /**
     * 用能趋势
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param type           type  0：时；1：日
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("用能趋势")
    @GetMapping("energyconsumptiontrend")
    public ResultResp<List<EnergyConsumptionTrendResp>> energyConsumptionTrend(@RequestParam(required = false) String sceneProjectId,
                                                                               @RequestParam(required = false) String sceneEdificeId,
                                                                               @RequestParam(required = false) String sceneFloorId,
                                                                               @RequestParam String type,
                                                                               @RequestParam(required = false) String startTime,
                                                                               @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<EnergyDataEntity> energyDataEntities = energyDataService.list(new QueryWrapper<EnergyDataEntity>()
                .select("solidify_date", "standard_coal")
                .eq(StringUtils.hasLength(sceneProjectId), "scene_project_id", sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), "scene_edifice_id", sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), "scene_floor_id", sceneFloorId)
                .ge(Objects.nonNull(start), "solidify_date", start)
                .le("solidify_date", end)
                .orderByAsc("solidify_date")
        );
        String dateFormat = "yyyy-MM-dd HH:00";
        if (StringUtils.pathEquals("1", type)) {
            dateFormat = "yyyy-MM-dd";
        }
        String unit = "kgce";
        String finalFormat = dateFormat;
        Map<String, List<EnergyDataEntity>> groupBySolidifyDateMap = energyDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), finalFormat), TreeMap::new, Collectors.toList()));
        List<EnergyConsumptionTrendResp.DataItem> dataDetailList = new ArrayList<>();
        for (Map.Entry<String, List<EnergyDataEntity>> entry : groupBySolidifyDateMap.entrySet()) {
            dataDetailList.add(new EnergyConsumptionTrendResp.DataItem()
                    .setTime(entry.getKey()).setValue(entry.getValue()
                            .stream()
                            .map(EnergyDataEntity::getStandardCoal)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .setScale(2, RoundingMode.HALF_DOWN)));
        }
        BigDecimal t = new BigDecimal("1000");
        if (dataDetailList.stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tce";
            for (EnergyConsumptionTrendResp.DataItem dataItem : dataDetailList) {
                dataItem.setValue(dataItem.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
            }
        }
        return ResultResp.success(Collections.singletonList(new EnergyConsumptionTrendResp()
                .setName("碳排放量")
                .setUnit(unit)
                .setData(dataDetailList)));
    }

    /**
     * 光伏发电情况-查看设备
     */
    @ApiOperation("光伏发电情况-查看设备")
    @GetMapping("photovoltaicPowerDevice")
    public ResultResp<List<PhotoVoltaicPowerDeviceResp>> photovoltaicPowerStatus(@RequestParam(required = false) String sceneProjectId,
                                                                                 @RequestParam(required = false) String sceneEdificeId,
                                                                                 @RequestParam(required = false) String sceneFloorId,
                                                                                 @RequestParam(required = false) String sceneRoomId) {
        List<ProductTypeEntity> productTypes = productTypeService.getPhotovoltaic();
        if (CollectionUtils.isEmpty(productTypes)) {
            return ResultResp.success(Collections.emptyList());
        }
        return ResultResp.success(deviceService.list(new LambdaQueryWrapper<DeviceEntity>()
                        .eq(StringUtils.hasLength(sceneProjectId), DeviceEntity::getSceneProjectId, sceneProjectId)
                        .eq(StringUtils.hasLength(sceneEdificeId), DeviceEntity::getSceneEdificeId, sceneEdificeId)
                        .eq(StringUtils.hasLength(sceneFloorId), DeviceEntity::getSceneFloorId, sceneFloorId)
                        .eq(StringUtils.hasLength(sceneRoomId), DeviceEntity::getSceneRoomId, sceneRoomId)
                        .in(DeviceEntity::getProductTypeId, productTypes.stream().map(ProductTypeEntity::getId).collect(Collectors.toSet()))
                        .orderByAsc(DeviceEntity::getDeviceCode)
                ).stream()
                .map(item -> new PhotoVoltaicPowerDeviceResp().
                        setId(item.getId())
                        .setName(item.getDeviceName()))
                .collect(Collectors.toList()));
    }

    /**
     * 光伏发电情况-查看设备数据
     *
     * @param id 设备ID
     */
    @ApiOperation("光伏发电情况-查看设备数据")
    @GetMapping("photovoltaicPowerDeviceData")
    public ResultResp<PhotoVoltaicPowerDeviceDataResp> photovoltaicPowerDeviceData(@RequestParam String id,
                                                                                   @RequestParam(required = false) String sceneProjectId,
                                                                                   @RequestParam(required = false) String sceneEdificeId,
                                                                                   @RequestParam(required = false) String sceneFloorId,
                                                                                   @RequestParam(required = false) String sceneRoomId) {
        List<MeterDeviceEntity> meterDeviceEntities = meterDeviceService.lambdaQuery()
                .eq(MeterDeviceEntity::getDeviceId, id)
                .eq(MeterDeviceEntity::getDelFlag, IMeterDeviceEnums.DelFlag.delFlag1.delFlag)
                .list();
        BigDecimal t = new BigDecimal("1000");
        String unit = "kWh";
        Set<String> meterDeviceIds = meterDeviceEntities.stream().map(BaseEntity::getId).collect(Collectors.toSet());
        List<EnergyDataEntity> energyDataEntities = meterDeviceIds.isEmpty() ? Collections.emptyList() : energyDataService.list(new LambdaQueryWrapper<EnergyDataEntity>()
                .in(EnergyDataEntity::getMeterDeviceId, meterDeviceIds)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType11.readMeterType)
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.POWER_GENERATION.getValue())
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneRoomId, sceneRoomId)
                .orderByAsc(EnergyDataEntity::getSolidifyDate)
        );
        PhotoVoltaicPowerDeviceDataResp dataResp = new PhotoVoltaicPowerDeviceDataResp();
        List<PhotoVoltaicPowerDeviceDataResp.DataItem> dataItems = new ArrayList<>();
        Map<String, List<EnergyDataEntity>> dataMap = energyDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM-dd HH:00"), TreeMap::new, Collectors.toList()));

        List<PhotoVoltaicPowerDeviceDataResp.DataItemDetail> dataItemDetails = new ArrayList<>();
        for (Map.Entry<String, List<EnergyDataEntity>> entry : dataMap.entrySet()) {
            dataItemDetails.add(new PhotoVoltaicPowerDeviceDataResp.DataItemDetail()
                    .setTime(entry.getKey())
                    .setValue(entry.getValue()
                            .stream()
                            .map(EnergyDataEntity::getMeterValue).filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .setScale(2, RoundingMode.HALF_DOWN)));
        }
        // 光伏发电量总量
        BigDecimal total1 = dataItemDetails.stream().map(PhotoVoltaicPowerDeviceDataResp.DataItemDetail::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        dataItems.add(new PhotoVoltaicPowerDeviceDataResp.DataItem().setName("光伏发电量").setUnit(unit).setData(dataItemDetails));

        energyDataEntities = meterDeviceIds.isEmpty() ? Collections.emptyList() : energyDataService.list(new LambdaQueryWrapper<EnergyDataEntity>()
                .in(EnergyDataEntity::getMeterDeviceId, meterDeviceIds)
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneRoomId, sceneRoomId)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType11.readMeterType)
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.ON_GRID_ENERGY.getValue())
                .orderByAsc(EnergyDataEntity::getSolidifyDate)
        );
        dataMap = energyDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM-dd HH:00"), TreeMap::new, Collectors.toList()));

        dataItemDetails = new ArrayList<>();
        for (Map.Entry<String, List<EnergyDataEntity>> entry : dataMap.entrySet()) {
            dataItemDetails.add(new PhotoVoltaicPowerDeviceDataResp.DataItemDetail()
                    .setTime(entry.getKey())
                    .setValue(entry.getValue()
                            .stream()
                            .map(EnergyDataEntity::getMeterValue)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .setScale(2, RoundingMode.HALF_DOWN)));
        }
        // 上网电量
        BigDecimal total2 = dataItemDetails.stream().map(PhotoVoltaicPowerDeviceDataResp.DataItemDetail::getValue)
                .reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_DOWN);
        dataItems.add(new PhotoVoltaicPowerDeviceDataResp.DataItem().setName("上网电量").setUnit(unit).setData(dataItemDetails));
        if (dataItems.stream().anyMatch(item -> item.getData().stream().anyMatch(dataItem -> dataItem.getValue().compareTo(t) >= 0))) {
            unit = "MWh";
            for (PhotoVoltaicPowerDeviceDataResp.DataItem dataItem : dataItems) {
                dataItem.setUnit(unit);
                for (PhotoVoltaicPowerDeviceDataResp.DataItemDetail item : dataItem.getData()) {
                    item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
                }
            }
        }
        dataResp.setData(dataItems);
        dataResp.setConsumptionRate(total1.doubleValue() == 0.0D ? BigDecimal.ZERO : (total1.subtract(total2)).divide(total1, 2, RoundingMode.HALF_UP));
        return ResultResp.success(dataResp);
    }

    /**
     * 储能使用情况
     */
    @ApiOperation("储能使用情况")
    @GetMapping("energyStorageUsage")
    public ResultResp<List<PhotoVoltaicPowerDeviceResp>> energyStorageUsage(@RequestParam(required = false) String sceneProjectId,
                                                                            @RequestParam(required = false) String sceneEdificeId,
                                                                            @RequestParam(required = false) String sceneFloorId,
                                                                            @RequestParam(required = false) String sceneRoomId) {
        List<ProductTypeEntity> energyStorages = productTypeService.getEnergyStorage();
        if (CollectionUtils.isEmpty(energyStorages)) {
            return ResultResp.success(new ArrayList<>());
        }
        return ResultResp.success(deviceService.list(new LambdaQueryWrapper<DeviceEntity>()
                        .eq(StringUtils.hasLength(sceneProjectId), DeviceEntity::getSceneProjectId, sceneProjectId)
                        .eq(StringUtils.hasLength(sceneEdificeId), DeviceEntity::getSceneEdificeId, sceneEdificeId)
                        .eq(StringUtils.hasLength(sceneFloorId), DeviceEntity::getSceneFloorId, sceneFloorId)
                        .eq(StringUtils.hasLength(sceneRoomId), DeviceEntity::getSceneRoomId, sceneRoomId)
                        .in(DeviceEntity::getProductTypeId, energyStorages.stream().map(ProductTypeEntity::getId).collect(Collectors.toSet()))
                        .orderByAsc(DeviceEntity::getDeviceCode)
                ).stream()
                .map(item -> new PhotoVoltaicPowerDeviceResp()
                        .setId(item.getId())
                        .setName(item.getDeviceName()))
                .collect(Collectors.toList()));
    }


    /**
     * 储能使用情况-查看设备数据
     *
     * @param id        设备ID
     * @param startTime 开始时间
     * @param endTime   结束时间
     */
    @ApiOperation("储能使用情况-查看设备数据")
    @GetMapping("energyStorageUsageData")
    public ResultResp<EnergyStorageUsageDataResp> energyStorageUsageData(@RequestParam String id,
                                                                         @RequestParam(required = false) String sceneProjectId,
                                                                         @RequestParam(required = false) String sceneEdificeId,
                                                                         @RequestParam(required = false) String sceneFloorId,
                                                                         @RequestParam(required = false) String sceneRoomId,
                                                                         @RequestParam(required = false) String startTime,
                                                                         @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<MeterDeviceEntity> meterDeviceEntities = meterDeviceService.lambdaQuery()
                .eq(MeterDeviceEntity::getDeviceId, id)
                .eq(MeterDeviceEntity::getDelFlag, IMeterDeviceEnums.DelFlag.delFlag1.delFlag)
                .list();
        BigDecimal t = new BigDecimal("1000");
        String unit = "kwh";
        Set<String> meterDeviceIds = meterDeviceEntities.stream().map(MeterDeviceEntity::getId).collect(Collectors.toSet());
        List<EnergyDataEntity> energyDataEntities = CollectionUtils.isEmpty(meterDeviceIds) ? Collections.emptyList() : energyDataService.list(new LambdaQueryWrapper<EnergyDataEntity>()
                .in(EnergyDataEntity::getMeterDeviceId, meterDeviceIds)
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneRoomId, sceneRoomId)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType12.readMeterType)
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.ENERGY_STORAGE_CHARGING.getValue())
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end)
                .orderByAsc(EnergyDataEntity::getSolidifyDate)
        );
        EnergyStorageUsageDataResp dataResp = new EnergyStorageUsageDataResp();
        List<EnergyStorageUsageDataResp.DataItem> dataItems = new ArrayList<>();
        Map<String, List<EnergyDataEntity>> dataMap = energyDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM-dd HH:00"), TreeMap::new, Collectors.toList()));

        List<PhotoVoltaicPowerDeviceDataResp.DataItemDetail> dataItemDetails = new ArrayList<>();
        for (Map.Entry<String, List<EnergyDataEntity>> entry : dataMap.entrySet()) {
            dataItemDetails.add(new PhotoVoltaicPowerDeviceDataResp.DataItemDetail().setTime(entry.getKey()).setValue(entry.getValue().stream()
                    .map(EnergyDataEntity::getMeterValue).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add)));
        }
        dataItems.add(new EnergyStorageUsageDataResp.DataItem().setName("储能充电量").setUnit(unit).setData(dataItemDetails));
        energyDataEntities = CollectionUtils.isEmpty(meterDeviceIds) ? Collections.emptyList() : energyDataService.list(new LambdaQueryWrapper<EnergyDataEntity>()
                .in(EnergyDataEntity::getMeterDeviceId, meterDeviceIds)
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneRoomId, sceneRoomId)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType12.readMeterType)
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.ENERGY_STORAGE_DISCHARGE.getValue())
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end)
                .orderByAsc(EnergyDataEntity::getSolidifyDate)
        );
        dataMap = energyDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM-dd HH:00"), TreeMap::new, Collectors.toList()));

        dataItemDetails = new ArrayList<>();
        for (Map.Entry<String, List<EnergyDataEntity>> entry : dataMap.entrySet()) {
            dataItemDetails.add(new PhotoVoltaicPowerDeviceDataResp.DataItemDetail()
                    .setTime(entry.getKey())
                    .setValue(entry.getValue().stream()
                            .map(EnergyDataEntity::getMeterValue)
                            .filter(Objects::nonNull)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .setScale(2, RoundingMode.HALF_DOWN)));
        }
        dataItems.add(new EnergyStorageUsageDataResp.DataItem()
                .setName("储能放电量")
                .setUnit(unit)
                .setData(dataItemDetails));
        if (dataItems.stream().anyMatch(item -> item.getData().stream().anyMatch(dataItem -> dataItem.getValue().compareTo(t) >= 0))) {
            unit = "MWh";
            for (EnergyStorageUsageDataResp.DataItem dataItem : dataItems) {
                dataItem.setUnit(unit);
                for (PhotoVoltaicPowerDeviceDataResp.DataItemDetail item : dataItem.getData()) {
                    item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
                }
            }
        }
        dataResp.setSeries(dataItems);

        BigDecimal value = CollectionUtils.isEmpty(meterDeviceIds) ? BigDecimal.ZERO : ObjectUtil.defaultIfNull(Joins.of(EnergyDataEntity.class)
                .selectAs(builder -> builder.add("SUM(meter_value)", "meterValue", false))
                .in(EnergyDataEntity::getMeterDeviceId, meterDeviceIds)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType12.readMeterType)
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.ENERGY_STORAGE_CHARGING.getValue())
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneRoomId, sceneRoomId)
                .ge(EnergyDataEntity::getSolidifyDate, LocalDate.now().with(TemporalAdjusters.firstDayOfYear()).atTime(LocalTime.MIN))
                .le(EnergyDataEntity::getSolidifyDate, LocalDate.now().with(TemporalAdjusters.lastDayOfYear()).atTime(LocalTime.MAX))
                .orderByAsc(EnergyDataEntity::getSolidifyDate).joinGetOne(BigDecimal.class), BigDecimal.ZERO);
        BigDecimal value2 = physicalModelService.queryValueByIdsAndDeviceIdAndName(id, null, "额定容量", start, end); // 管理后台-设备列表-运行状态-额定容量
        // 储能利用率数据来源：储能利用率=储能设备年累计充电量/（储能额定容量*365）*100%
        dataResp.setEnergyStorage(value2.doubleValue() == 0.0F ? BigDecimal.ZERO : value.divide(value2.multiply(new BigDecimal("365")), 2, RoundingMode.HALF_DOWN)
                .divide(new BigDecimal("100"), 2, RoundingMode.HALF_DOWN));
        dataResp.setChargingCycles(physicalModelService.queryValueByIdsAndDeviceIdAndName(id, null, "充电量", start, end).intValue());
        dataResp.setDischargeFrequency(physicalModelService.queryValueByIdsAndDeviceIdAndName(id, null, "放电量", start, end).intValue());
        return ResultResp.success(dataResp);
    }

    /**
     * 柔性度源调度
     */
    @ApiOperation("柔性度源调度")
    @GetMapping("flexibleSource")
    public ResultResp<List<FlexibleSourceResp>> flexibleSource() {
        List<FlexibleSourceResp> dataList = productTypeService.lambdaQuery()
                .in(ProductTypeEntity::getProductTypeName, Arrays.asList("照明", "充电桩", "热储", "储能"))
                .list()
                .stream().map(item -> new FlexibleSourceResp().setId(item.getId()).setName(item.getProductTypeName()))
                .collect(Collectors.toList());
        return ResultResp.success(dataList);
    }

    /**
     * 柔性度源调度情况-图表数据
     *
     * @param id productTypeId
     */
    @ApiOperation("柔性度源调度情况-图表数据")
    @GetMapping("flexibleSourceData")
    public ResultResp<List<FlexibleSourceDataResp>> flexibleSourceData(@RequestParam String id) {
        List<FlexibleSourceDataResp> list = new ArrayList<>();
        String unit = "kW";
        list.add(new FlexibleSourceDataResp().setName("最大可调功率").setUnit(unit).setData(Arrays.asList(
                new FlexibleSourceDataResp.DataItem().setValue(BigDecimal.ZERO).setTime("2024-06-01 10:00:00"),
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("23")).setTime("2024-06-01 11:00:00"),
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("29")).setTime("2024-06-01 12:00:00")
        )));
        list.add(new FlexibleSourceDataResp().setName("最小可调功率").setUnit(unit).setData(Arrays.asList(
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("40")).setTime("2024-06-01 10:00:00"),
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("50")).setTime("2024-06-01 11:00:00"),
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("70")).setTime("2024-06-01 12:00:00")
        )));
        list.add(new FlexibleSourceDataResp().setName("标准运行功率").setUnit(unit).setData(Arrays.asList(
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("39")).setTime("2024-06-01 10:00:00"),
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("60")).setTime("2024-06-01 11:00:00"),
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("79")).setTime("2024-06-01 12:00:00")
        )));
        list.add(new FlexibleSourceDataResp().setName("柔性调度功率").setUnit(unit).setData(Arrays.asList(
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("70")).setTime("2024-06-01 10:00:00"),
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("41")).setTime("2024-06-01 11:00:00"),
                new FlexibleSourceDataResp.DataItem().setValue(new BigDecimal("81")).setTime("2024-06-01 12:00:00")
        )));
        return ResultResp.success(list);
    }

    /**
     * 柔性度分析
     */
    @ApiOperation("柔性度分析")
    @GetMapping("flexibleAnalysis")
    public ResultResp<List<FlexibleAnalysisResp>> flexibleAnalysis() {
        List<FlexibleAnalysisResp> list = new ArrayList<>();
        String unit = "kW";
        list.add(new FlexibleAnalysisResp().setName("照明").setUnit(unit).setData(Arrays.asList(
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("52")).setTime("2024-06-01 10:00:00"),
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("29")).setTime("2024-06-01 11:00:00"),
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("66")).setTime("2024-06-01 12:00:00")
        )));
        list.add(new FlexibleAnalysisResp().setName("充电桩").setUnit(unit).setData(Arrays.asList(
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("28")).setTime("2024-06-01 10:00:00"),
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("51")).setTime("2024-06-01 11:00:00"),
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("70")).setTime("2024-06-01 12:00:00")
        )));
        list.add(new FlexibleAnalysisResp().setName("热储").setUnit(unit).setData(Arrays.asList(
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("39")).setTime("2024-06-01 10:00:00"),
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("43")).setTime("2024-06-01 11:00:00"),
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("79")).setTime("2024-06-01 12:00:00")
        )));
        list.add(new FlexibleAnalysisResp().setName("储能").setUnit(unit).setData(Arrays.asList(
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("70")).setTime("2024-06-01 10:00:00"),
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("20")).setTime("2024-06-01 11:00:00"),
                new FlexibleAnalysisResp.DataItem().setValue(new BigDecimal("33")).setTime("2024-06-01 12:00:00")
        )));
        return ResultResp.success(list);
    }

    /**
     * 碳排放分项统计
     * 查询范围
     */
    @ApiOperation("碳排放分项统计")
    @GetMapping("carbonEmissionRange")
    public ResultResp<List<CarbonEmissionRangeResp>> carbonEmissionRange(@RequestParam(required = false) String sceneProjectId,
                                                                         @RequestParam(required = false) String sceneEdificeId,
                                                                         @RequestParam(required = false) String sceneFloorId,
                                                                         @RequestParam(required = false) String sceneRoomId) {
        List<CarbonEmissionRangeEntity> rangeEntities = carbonEmissionRangeService.list(new LambdaQueryWrapper<CarbonEmissionRangeEntity>()
                .orderByAsc(CarbonEmissionRangeEntity::getRangeName));
        List<CarbonEmissionRangeResp> list = new ArrayList<>();
        String unit = "kgCO2";
        BigDecimal t = new BigDecimal("1000");
        for (CarbonEmissionRangeEntity item : rangeEntities) {
            BigDecimal value = Joins.of(EnergyDataEntity.class)
                    .selectAs(consumer -> consumer.add("SUM(carbon_emission)", "carbonEmission", false))
                    .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                    .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                    .end()
                    .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                    .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                    .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                    .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneRoomId, sceneRoomId)
                    .eq(EnergyDataEntity::getCalculationRange, item.getId())
                    .joinGetOne(BigDecimal.class);
            list.add(new CarbonEmissionRangeResp()
                    .setId(item.getId()).
                    setName(item.getRangeName())
                    .setUnit(unit)
                    .setValue(ObjectUtil.defaultIfNull(value, BigDecimal.ZERO)
                            .setScale(2, RoundingMode.HALF_DOWN)));
        }
        if (list.stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tCO2";
            for (CarbonEmissionRangeResp item : list) {
                item.setUnit(unit);
                item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
            }
        }
        return ResultResp.success(list);
    }


    /**
     * 碳排放分项统计- 查询指定范围下的分类数据
     */
    @ApiOperation("碳排放分项统计- 查询指定范围下的分类数据")
    @GetMapping("carbonEmissionRangeData")
    public ResultResp<List<CarbonEmissionRangeDataResp>> carbonEmissionDataStatistics(@RequestParam String id,
                                                                                      @RequestParam(required = false) String sceneProjectId,
                                                                                      @RequestParam(required = false) String sceneEdificeId,
                                                                                      @RequestParam(required = false) String sceneFloorId,
                                                                                      @RequestParam(required = false) String sceneRoomId) {
        List<EnergyConsumptionTypeEntity> consumptionTypeEntities = energyConsumptionTypeService.list(new LambdaQueryWrapper<EnergyConsumptionTypeEntity>()
                .eq(EnergyConsumptionTypeEntity::getRangeId, id)
                .eq(EnergyConsumptionTypeEntity::getStatus, "1")
                .orderByAsc(EnergyConsumptionTypeEntity::getCreateTm));
        String unit = "kgCO2";
        BigDecimal t = new BigDecimal("1000");
        List<EnergyDataEntity> list = energyDataService.list(new LambdaQueryWrapper<EnergyDataEntity>()
                .select(EnergyDataEntity::getEnergyTypeId, EnergyDataEntity::getCarbonEmission)
                .eq(EnergyDataEntity::getCalculationRange, id)
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneRoomId, sceneRoomId)
                .isNotNull(EnergyDataEntity::getEnergyTypeId)
        );
        List<CarbonEmissionRangeDataResp> respList = new ArrayList<>();
        for (EnergyConsumptionTypeEntity item : consumptionTypeEntities) {
            respList.add(new CarbonEmissionRangeDataResp().setId(item.getId())
                    .setName(item.getEnergyName())
                    .setUnit(unit)
                    .setValue(
                            list.stream()
                                    .filter(energyDataEntity -> StringUtils.pathEquals(energyDataEntity.getEnergyTypeId(), item.getId()))
                                    .map(EnergyDataEntity::getCarbonEmission).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add)
                                    .setScale(2, RoundingMode.HALF_DOWN)
                    ));
        }
        if (respList.stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tCO2";
            for (CarbonEmissionRangeDataResp item : respList) {
                item.setUnit(unit);
                item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
            }
        }
        return ResultResp.success(respList);
    }

    /**
     * 碳排放分类统计
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("碳排放分类统计")
    @GetMapping("carbonEmissionsClassification")
    public ResultResp<List<CarbonEmissionsClassificationResp>> carbonEmissionsClassification(@RequestParam(required = false) String sceneProjectId,
                                                                                             @RequestParam(required = false) String sceneEdificeId,
                                                                                             @RequestParam(required = false) String sceneFloorId,
                                                                                             @RequestParam(required = false) String startTime,
                                                                                             @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<ProductTypeEntity> productTypeEntities = productTypeService.list(new LambdaQueryWrapper<ProductTypeEntity>()
                .eq(ProductTypeEntity::getActiveStatus, ProductTypeActiveStatus.ENABLED.getValue())
                .orderByAsc(ProductTypeEntity::getSort));
        String unit = "kgCO2";
        BigDecimal t = new BigDecimal("1000");
        List<EnergyDataEntity> energyDataEntities = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer ->
                        consumer
                                .add("ecp_tt_energy_data.product_type_id", "productTypeId", false)
                                .add("ecp_tt_energy_data.scene_edifice_id", "sceneEdificeId", false)
                                .add("ecp_tt_energy_data.scene_floor_id", "sceneFloorId", false)
                                .add("ecp_tt_energy_data.scene_room_id", "sceneRoomId", false)
                                .add("SUM(carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .isNotNull(EnergyDataEntity::getProductTypeId)
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end)
                .groupBy(Arrays.asList(EnergyDataEntity::getProductTypeId, EnergyDataEntity::getSceneEdificeId, EnergyDataEntity::getSceneFloorId, EnergyDataEntity::getSceneRoomId))
                .joinList(EnergyDataEntity.class);
        List<CarbonEmissionsClassificationResp> classificationResps = new ArrayList<>();
        List<String> projectIds = energyDataEntities.stream().map(EnergyDataEntity::getSceneProjectId).collect(Collectors.toList());
        Map<String, SceneProjectEntity> projectEntityMap = projectIds.isEmpty() ? Collections.emptyMap() : sceneProjectService.listByIds(projectIds).stream().collect(Collectors.toMap(SceneProjectEntity::getId, Function.identity()));
        List<String> edificeIds = energyDataEntities.stream().map(EnergyDataEntity::getSceneEdificeId).collect(Collectors.toList());
        Map<String, SceneEdificeEntity> edificeEntityMap = edificeIds.isEmpty() ? Collections.emptyMap() : sceneEdificeService.listByIds(edificeIds).stream().collect(Collectors.toMap(SceneEdificeEntity::getId, Function.identity()));
        List<String> floorIds = energyDataEntities.stream().map(EnergyDataEntity::getSceneFloorId).collect(Collectors.toList());
        Map<String, SceneFloorEntity> floorEntityMap = floorIds.isEmpty() ? Collections.emptyMap() : sceneFloorService.listByIds(floorIds).stream().collect(Collectors.toMap(SceneFloorEntity::getId, Function.identity()));
        List<String> roomIds = energyDataEntities.stream().map(EnergyDataEntity::getSceneRoomId).collect(Collectors.toList());
        Map<String, SceneRoomEntity> roomEntityMap = roomIds.isEmpty() ? Collections.emptyMap() : sceneRoomService.listByIds(roomIds).stream().collect(Collectors.toMap(SceneRoomEntity::getId, Function.identity()));
        for (ProductTypeEntity productTypeEntity : productTypeEntities) {
            List<EnergyDataEntity> dataEntities = energyDataEntities.stream()
                    .filter(item -> StringUtils.pathEquals(item.getProductTypeId(), productTypeEntity.getId())).collect(Collectors.toList());
            classificationResps.add(new CarbonEmissionsClassificationResp()
                    .setName(productTypeEntity.getProductTypeName())
                    .setUnit(unit)
                    .setData(dataEntities.stream()
                            .map(item -> new CarbonEmissionsClassificationResp.DataItem()
                                    .setProjectId(item.getSceneProjectId())
                                    .setProjectName(projectEntityMap.containsKey(item.getSceneProjectId()) ? projectEntityMap.get(item.getSceneProjectId()).getProjectName() : null)
                                    .setSceneEdificeId(item.getSceneEdificeId())
                                    .setSceneEdificeName(edificeEntityMap.containsKey(item.getSceneEdificeId()) ? edificeEntityMap.get(item.getSceneEdificeId()).getEdificeName() : null)
                                    .setSceneFloorId(item.getSceneFloorId())
                                    .setSceneFloorName(floorEntityMap.containsKey(item.getSceneFloorId()) ? floorEntityMap.get(item.getSceneFloorId()).getFloorName() : null)
                                    .setSceneRoomId(item.getSceneRoomId())
                                    .setSceneRoomName(roomEntityMap.containsKey(item.getSceneRoomId()) ? roomEntityMap.get(item.getSceneRoomId()).getRoomName() : null)
                                    .setValue(item.getCarbonEmission()
                                            .setScale(2, RoundingMode.HALF_DOWN)))
                            .collect(Collectors.toList())));
        }
        if (classificationResps.stream().anyMatch(item -> item.getData().stream().anyMatch(dataItem -> dataItem.getValue().compareTo(t) >= 0))) {
            unit = "tCO2";
            for (CarbonEmissionsClassificationResp classificationResp : classificationResps) {
                classificationResp.setUnit(unit);
                for (CarbonEmissionsClassificationResp.DataItem dataItem : classificationResp.getData()) {
                    dataItem.setValue(dataItem.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
                }
            }
        }
        return ResultResp.success(classificationResps);
    }

    /**
     * 能源碳排放统计
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("能源碳排放统计")
    @GetMapping("energyCarbonEmissionsStatistics")
    public ResultResp<List<EnergyCarbonEmissionsStatisticsResp>> energyCarbonEmissionsStatistics(@RequestParam(required = false) String sceneProjectId,
                                                                                                 @RequestParam(required = false) String sceneEdificeId,
                                                                                                 @RequestParam(required = false) String sceneFloorId,
                                                                                                 @RequestParam(required = false) String startTime,
                                                                                                 @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<EnergyConsumptionTypeEntity> consumptionTypeEntities = energyConsumptionTypeService.list(new LambdaQueryWrapper<EnergyConsumptionTypeEntity>()
                .eq(EnergyConsumptionTypeEntity::getStatus, "1")
                .orderByAsc(EnergyConsumptionTypeEntity::getCreateTm));
        List<EnergyDataEntity> energyDataEntities = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer ->
                        consumer
                                .add("ecp_tt_energy_data.energy_type_id", "energyTypeId", false)
                                .add("SUM(carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(EnergyDataEntity::getEnergyTypeId, energyConsumptionTypeService.getElectricId())
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end)
                .groupBy(EnergyDataEntity::getEnergyTypeId)
                .joinList(EnergyDataEntity.class);
        String unit = "kgCO2";
        BigDecimal t = new BigDecimal("1000");
        List<EnergyCarbonEmissionsStatisticsResp> respList = new ArrayList<>();
        List<EnergyCarbonEmissionsStatisticsResp.DataItem> list = consumptionTypeEntities.stream()
                .map(item -> new EnergyCarbonEmissionsStatisticsResp.DataItem()
                        .setName(item.getEnergyName())
                        .setValue(energyDataEntities.stream().filter(energyData -> StringUtils.pathEquals(energyData.getEnergyTypeId(), item.getId()))
                                .map(EnergyDataEntity::getCarbonEmission)
                                .reduce(BigDecimal.ZERO, BigDecimal::add)
                                .setScale(2, RoundingMode.HALF_DOWN)))
                .collect(Collectors.toList());
        respList.add(new EnergyCarbonEmissionsStatisticsResp().setName("市电").setUnit(unit).setData(list));

        // 光伏发电使用量
        List<EnergyDataEntity> energyDataEntities2 = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer ->
                        consumer
                                .add("ecp_tt_energy_data.energy_type_id", "energyTypeId", false)
                                .add("SUM(meter_value)", "meter_value", false)
                )
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType11.readMeterType)
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.POWER_GENERATION.getValue())
                .eq(EnergyDataEntity::getEnergyTypeId, energyConsumptionTypeService.getElectricId())
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end)
                .groupBy(EnergyDataEntity::getEnergyTypeId)
                .joinList(EnergyDataEntity.class);
        List<EnergyDataEntity> energyDataEntities3 = Joins.of(EnergyDataEntity.class)
                .selectAs(consumer ->
                        consumer
                                .add("ecp_tt_energy_data.energy_type_id", "energyTypeId", false)
                                .add("SUM(meter_value)", "meter_value", false)
                )
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .eq(EnergyDataEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.readMeterType11.readMeterType)
                .eq(EnergyDataEntity::getStaticsType, MeterDeviceStaticsTypeEnums.ON_GRID_ENERGY.getValue())
                .eq(EnergyDataEntity::getEnergyTypeId, energyConsumptionTypeService.getElectricId())
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end)
                .groupBy(EnergyDataEntity::getEnergyTypeId)
                .joinList(EnergyDataEntity.class);
        respList.add(new EnergyCarbonEmissionsStatisticsResp()
                .setName("光伏发电使用量")
                .setUnit(unit)
                .setData(consumptionTypeEntities.stream()
                        .map(item -> {
                            BigDecimal value = energyDataEntities2.stream().filter(energyDataEntity -> item.getId().equals(energyDataEntity.getEnergyTypeId()))
                                    .findFirst().map(EnergyDataEntity::getMeterValue).orElse(BigDecimal.ZERO);
                            BigDecimal value2 = energyDataEntities3.stream().filter(energyDataEntity -> item.getId().equals(energyDataEntity.getEnergyTypeId()))
                                    .findFirst().map(EnergyDataEntity::getMeterValue).orElse(BigDecimal.ZERO);
                            return new EnergyCarbonEmissionsStatisticsResp.DataItem().setName(item.getEnergyName())
                                    .setValue(value.subtract(value2).setScale(2, RoundingMode.HALF_DOWN));
                        }).collect(Collectors.toList())));


        // 外购绿电
        ActivityCarbonEmissionEntity activityCarbonEmission = activityCarbonEmissionService.findFirstByActivityName("外购绿电");
        List<EnergyCarbonEmissionsStatisticsResp.DataItem> dataList = Collections.emptyList();
        if (Objects.nonNull(activityCarbonEmission)) {
            List<DataFillingDataEntity> dataFillingDataEntities = dataFillingDataService.lambdaQuery()
                    .select(DataFillingDataEntity::getEnergyConsumptionTypeId, DataFillingDataEntity::getCarbonEmission)
                    .eq(DataFillingDataEntity::getActivityCarbonEmissionId, activityCarbonEmission.getId())
                    .eq(StringUtils.hasLength(sceneProjectId), DataFillingDataEntity::getSceneProjectId, sceneProjectId)
                    .eq(StringUtils.hasLength(sceneEdificeId), DataFillingDataEntity::getSceneEdificeId, sceneEdificeId)
                    .eq(StringUtils.hasLength(sceneFloorId), DataFillingDataEntity::getSceneFloorId, sceneFloorId)
                    .ge(Objects.nonNull(start), DataFillingDataEntity::getSolidifyDate, start)
                    .le(DataFillingDataEntity::getSolidifyDate, end).list();
            dataList = consumptionTypeEntities.stream()
                    .map(item -> {
                        BigDecimal value = dataFillingDataEntities.stream()
                                .filter(energyDataEntity -> item.getId().equals(energyDataEntity.getEnergyConsumptionTypeId()))
                                .map(DataFillingDataEntity::getCarbonEmission).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                        return new EnergyCarbonEmissionsStatisticsResp.DataItem().setName(item.getEnergyName())
                                .setValue(value.setScale(2, RoundingMode.HALF_DOWN));
                    }).collect(Collectors.toList());
        }
        respList.add(new EnergyCarbonEmissionsStatisticsResp().setName("外购绿电").setUnit(unit).setData(dataList));
        if (respList.stream().anyMatch(item -> item.getData().stream().anyMatch(itemData -> itemData.getValue().compareTo(t) >= 0))) {
            unit = "tCO2";
            for (EnergyCarbonEmissionsStatisticsResp item : respList) {
                item.setUnit(unit);
                for (EnergyCarbonEmissionsStatisticsResp.DataItem dataItem : item.getData()) {
                    dataItem.setValue(dataItem.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
                }
            }
        }
        return ResultResp.success(respList);
    }

    /**
     * 活动碳排放统计
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("活动碳排放统计")
    @GetMapping("activityCarbonEmissionsStatistics")
    public ResultResp<List<ActivityCarbonEmissionsStatisticsResp>> activityCarbonEmissionsStatistics(@RequestParam(required = false) String sceneProjectId,
                                                                                                     @RequestParam(required = false) String sceneEdificeId,
                                                                                                     @RequestParam(required = false) String sceneFloorId,
                                                                                                     @RequestParam(required = false) String startTime,
                                                                                                     @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<ActivityCarbonEmissionEntity> activityCarbonEmissionEntities = activityCarbonEmissionService.list(new LambdaQueryWrapper<ActivityCarbonEmissionEntity>()
                .orderByAsc(ActivityCarbonEmissionEntity::getActivityName));
        String unit = "kgCO2";
        BigDecimal t = new BigDecimal("1000");
        List<CarbonEmissionDataActivityResp> dataList = Joins.of(EnergyDataEntity.class)
                .select(EnergyDataEntity::getCarbonEmission)
                .innerJoin(ActivityCarbonEmissionDataEntity.class, ActivityCarbonEmissionDataEntity::getTargetId,
                        EnergyDataEntity::getMeterDeviceId)
                .selectAs(ActivityCarbonEmissionDataEntity::getActivityCarbonId, CarbonEmissionDataActivityResp::getActivityCarbonEmissionId)
                .end()
                .innerJoin(ActivityCarbonEmissionEntity.class, ActivityCarbonEmissionEntity::getId, ActivityCarbonEmissionDataEntity::getActivityCarbonId)
                .selectAs(ActivityCarbonEmissionEntity::getActivityName, CarbonEmissionDataActivityResp::getActivityCarbonEmissionName)
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                .le(EnergyDataEntity::getSolidifyDate, end).joinList(CarbonEmissionDataActivityResp.class);

        List<ActivityCarbonEmissionsStatisticsResp> statisticsResps = new ArrayList<>();
        Set<String> aceIds = dataList.stream()
                .map(CarbonEmissionDataActivityResp::getActivityCarbonEmissionId).collect(Collectors.toSet());
        List<ActivityCarbonEmissionEntity> filterEntities = activityCarbonEmissionEntities
                .stream()
                .filter(item -> aceIds.contains(item.getId())).collect(Collectors.toList());
        for (ActivityCarbonEmissionEntity carbonEmissionEntity : filterEntities) {
            BigDecimal value = dataList.stream()
                    .filter(item -> StringUtils.pathEquals(carbonEmissionEntity.getId(), item.getActivityCarbonEmissionId()))
                    .map(CarbonEmissionDataActivityResp::getCarbonEmission)
                    .filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            statisticsResps.add(new ActivityCarbonEmissionsStatisticsResp()
                    .setName(carbonEmissionEntity.getActivityName())
                    .setUnit(unit)
                    .setValue(value.setScale(2, RoundingMode.HALF_DOWN)));
        }

        if (statisticsResps.stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tCO2";
            for (ActivityCarbonEmissionsStatisticsResp item : statisticsResps) {
                item.setUnit(unit);
                item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
            }
        }
        return ResultResp.success(statisticsResps);
    }

    /**
     * 碳排放总量/碳排放强度趋势
     *
     * @param sceneProjectId sceneProjectId
     * @param type           0：碳排放总量；1：碳排放强度趋势
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("碳排放总量/碳排放强度趋势")
    @GetMapping("carbonEmissionsTotalOrIntensity")
    public ResultResp<CarbonEmissionsTotalOrIntensityResp> carbonEmissionsTotalOrIntensity(@RequestParam(required = false) String sceneProjectId,
                                                                                           @RequestParam(required = false) String sceneEdificeId,
                                                                                           @RequestParam(required = false) String sceneFloorId,
                                                                                           @RequestParam(required = false) String sceneCompanyId,
                                                                                           @RequestParam String type,
                                                                                           @RequestParam(required = false) String startTime,
                                                                                           @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        CarbonEmissionsTotalOrIntensityResp resp = new CarbonEmissionsTotalOrIntensityResp();
        String unit = "kgCO2";
        BigDecimal t = new BigDecimal("1000");
        if (StringUtils.pathEquals(type, "0")) {
            resp.setName("碳排放总量");
            List<EnergyDataEntity> energyDataEntities = Joins.of(EnergyDataEntity.class)
                    .select(EnergyDataEntity::getSolidifyDate, EnergyDataEntity::getCarbonEmission)
                    .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                    .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                    .end()
                    .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                    .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                    .eq(StringUtils.hasLength(sceneCompanyId), EnergyDataEntity::getSceneCompanyId, sceneCompanyId)
                    .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                    .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                    .le(EnergyDataEntity::getSolidifyDate, end)
                    .joinList(EnergyDataEntity.class);
            Map<String, List<EnergyDataEntity>> dateMap = energyDataEntities.stream()
                    .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM-dd HH:00"), TreeMap::new, Collectors.toList()));
            List<CarbonEmissionsTotalOrIntensityResp.DataItem> dataItems = new ArrayList<>();
            for (Map.Entry<String, List<EnergyDataEntity>> entry : dateMap.entrySet()) {
                BigDecimal value = entry.getValue().stream()
                        .map(EnergyDataEntity::getCarbonEmission)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                dataItems.add(new CarbonEmissionsTotalOrIntensityResp.DataItem().setTime(entry.getKey())
                        .setValue(value.setScale(2, RoundingMode.HALF_DOWN)));
            }
            resp.setData(dataItems);
        } else if (StringUtils.pathEquals(type, "1")) {
            resp.setName("碳排放强度趋势");
            List<EmissionIntensityDataResp> energyDataEntities = Joins.of(EnergyDataEntity.class)
                    .selectAll()
                    .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                    .selectAs(columnBuilder -> columnBuilder.addFunAlias(MeterDeviceEntity::getDeviceCode, EnergyDataExtEntity::getDeviceCode)
                            .addFunAlias(MeterDeviceEntity::getDeviceName, EnergyDataExtEntity::getDeviceName)
                            .addFunAlias(MeterDeviceEntity::getReadMeterType, EnergyDataExtEntity::getReadMeterType)
                            .addFunAlias(MeterDeviceEntity::getReadMeterTag, EnergyDataExtEntity::getReadMeterTag)
                    )
                    .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                    .end()
                    .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, EnergyDataEntity::getSceneProjectId)
                    .selectAs(build ->
                            build
                                    .add("ecp_tt_scene_project.project_name", "project_name", false)
                                    .add("ecp_tt_scene_project.floor_area", "project_floor_area", false)
                    )
                    .end()
                    .leftJoin(SceneEdificeEntity.class, SceneEdificeEntity::getId, EnergyDataEntity::getSceneEdificeId)
                    .selectAs(SceneEdificeEntity::getFloorArea, EmissionIntensityDataResp::getEdificeFloorArea)
                    .end()
                    .leftJoin(SceneFloorEntity.class, SceneFloorEntity::getId, EnergyDataEntity::getSceneFloorId)
                    .selectAs(SceneFloorEntity::getFloorArea, EmissionIntensityDataResp::getFloorArea)
                    .end()
                    .leftJoin(SceneRoomEntity.class, SceneRoomEntity::getId, EnergyDataEntity::getSceneRoomId)
                    .selectAs(SceneRoomEntity::getRoomArea, EmissionIntensityDataResp::getArea)
                    .end()
                    .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                    .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getSceneEdificeId, sceneEdificeId)
                    .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneFloorId, sceneFloorId)
                    .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                    .le(EnergyDataEntity::getSolidifyDate, end)
                    .joinList(EmissionIntensityDataResp.class);
            Map<String, List<EmissionIntensityDataResp>> dateMap = energyDataEntities.stream()
                    .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM-dd HH:00"), TreeMap::new, Collectors.toList()));
            List<CarbonEmissionsTotalOrIntensityResp.DataItem> dataItems = new ArrayList<>();
            for (Map.Entry<String, List<EmissionIntensityDataResp>> entry : dateMap.entrySet()) {
                BigDecimal value = entry.getValue().stream()
                        .map(item -> {
                            BigDecimal area = item.getArea();
                            if (Objects.isNull(area)) {
                                area = item.getFloorArea();
                            }
                            if (Objects.isNull(area)) {
                                area = item.getEdificeFloorArea();
                            }
                            if (Objects.isNull(area)) {
                                area = item.getProjectFloorArea();
                            }
                            return item.getCarbonEmission().divide(area, 2, RoundingMode.HALF_DOWN);
                        })
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                dataItems.add(new CarbonEmissionsTotalOrIntensityResp.DataItem().setTime(entry.getKey())
                        .setValue(value.setScale(2, RoundingMode.HALF_DOWN)));
            }
            resp.setData(dataItems);
        }
        if (resp.getData().stream().anyMatch(item -> item.getValue().compareTo(t) >= 0)) {
            unit = "tCO2";
            for (CarbonEmissionsTotalOrIntensityResp.DataItem item : resp.getData()) {
                item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
            }
        }
        resp.setUnit(unit);
        return ResultResp.success(resp);
    }


    /**
     * 碳减统计
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param type           0：碳减；1：碳汇
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("碳减统计")
    @GetMapping("carbonReductionStatistics")
    public ResultResp<List<CarbonReductionStatisticsResp>> carbonReductionStatistics(@RequestParam(required = false) String sceneProjectId,
                                                                                     @RequestParam(required = false) String sceneEdificeId,
                                                                                     @RequestParam(required = false) String sceneFloorId,
                                                                                     @RequestParam String type,
                                                                                     @RequestParam(required = false) String startTime,
                                                                                     @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<CarbonReductionStatisticsResp> dataList = new ArrayList<>();
        String unit = "kgCO2";
        BigDecimal t = new BigDecimal("1000");
        if ("0".equals(type)) {
            List<ProductTypeEntity> productTypeEntities = productTypeService.list(new LambdaQueryWrapper<ProductTypeEntity>()
                    .eq(ProductTypeEntity::getActiveStatus, ProductTypeActiveStatus.ENABLED.getValue())
                    .orderByAsc(ProductTypeEntity::getSort));
            List<EnergyDataEntity> energyDataEntities = Joins.of(EnergyDataEntity.class)
                    .selectAs(builder -> builder.add("ecp_tt_energy_data.product_type_id", "product_type_id", false)
                            .add("carbon_emission", "carbon_emission", false)
                            .add("solidify_date", "solidify_date", false)
                    )
                    .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                    .in(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                    .end()
                    .isNotNull(EnergyDataEntity::getProductTypeId)
                    .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                    .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getEnergyTypeId, sceneEdificeId)
                    .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneEdificeId, sceneFloorId)
                    .ge(Objects.nonNull(start), EnergyDataEntity::getSolidifyDate, start)
                    .le(EnergyDataEntity::getSolidifyDate, end).joinList(EnergyDataEntity.class);
            TreeMap<String, List<EnergyDataEntity>> dataMap = energyDataEntities.stream().collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM-dd HH:00"), TreeMap::new, Collectors.toList()));
            for (ProductTypeEntity productType : productTypeEntities) {
                List<CarbonReductionStatisticsResp.DataItem> items = new ArrayList<>();
                for (Map.Entry<String, List<EnergyDataEntity>> entry : dataMap.entrySet()) {
                    String date = entry.getKey();
                    BigDecimal value = entry.getValue().stream()
                            .filter(item -> item.getProductTypeId().equals(productType.getId()))
                            .map(EnergyDataEntity::getCarbonEmission)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .setScale(2, RoundingMode.HALF_DOWN);
                    items.add(new CarbonReductionStatisticsResp.DataItem().setTime(date).setValue(value));
                }
                dataList.add(new CarbonReductionStatisticsResp()
                        .setData(items)
                        .setUnit(unit)
                        .setName(productType.getProductTypeName()));
            }
        } else if ("1".equals(type)) {
            List<CarbonSinkEntity> sinkEntities = carbonSinkService.lambdaQuery().list();
            Map<String, List<CarbonSinkEntity>> typeMap = sinkEntities.stream().collect(Collectors.groupingBy(CarbonSinkEntity::getType));
            List<CarbonSinkDataEntity> carbonSinkDataEntities = carbonSinkDataService.list(new LambdaQueryWrapper<CarbonSinkDataEntity>()
                    .select(CarbonSinkDataEntity::getType, CarbonSinkDataEntity::getSolidifyDate, CarbonSinkDataEntity::getCarbonEmission)
                    .isNotNull(CarbonSinkDataEntity::getType)
                    .eq(StringUtils.hasLength(sceneProjectId), CarbonSinkDataEntity::getSceneProjectId, sceneProjectId)
                    .eq(StringUtils.hasLength(sceneEdificeId), CarbonSinkDataEntity::getSceneEdificeId, sceneEdificeId)
                    .eq(StringUtils.hasLength(sceneFloorId), CarbonSinkDataEntity::getSceneFloorId, sceneFloorId)
                    .ge(Objects.nonNull(start), CarbonSinkDataEntity::getSolidifyDate, start)
                    .le(CarbonSinkDataEntity::getSolidifyDate, end)
                    .orderByAsc(CarbonSinkDataEntity::getSolidifyDate));
            TreeMap<String, List<CarbonSinkDataEntity>> dataMap = carbonSinkDataEntities.stream()
                    .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM-dd HH:00"), TreeMap::new, Collectors.toList()));
            for (Map.Entry<String, List<CarbonSinkEntity>> entry : typeMap.entrySet()) {
                List<CarbonReductionStatisticsResp.DataItem> items = new ArrayList<>();
                Set<String> sinkDataIds = entry.getValue().stream().map(CarbonSinkEntity::getId).collect(Collectors.toSet());
                for (Map.Entry<String, List<CarbonSinkDataEntity>> sinkDataEntry : dataMap.entrySet()) {
                    String date = sinkDataEntry.getKey();
                    BigDecimal value = sinkDataEntry.getValue().stream()
                            .filter(item -> sinkDataIds.contains(item.getType()))
                            .map(CarbonSinkDataEntity::getCarbonEmission)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .setScale(2, RoundingMode.HALF_DOWN);
                    items.add(new CarbonReductionStatisticsResp.DataItem()
                            .setTime(date)
                            .setValue(value));
                }
                dataList.add(new CarbonReductionStatisticsResp().setData(items).setUnit(unit).setName(entry.getKey()));
            }
        }
        if (dataList.stream().anyMatch(item -> item.getData().stream().anyMatch(dataItem -> dataItem.getValue().compareTo(t) >= 0))) {
            unit = "tCO2";
            for (CarbonReductionStatisticsResp statisticsResp : dataList) {
                statisticsResp.setUnit(unit);
                for (CarbonReductionStatisticsResp.DataItem item : statisticsResp.getData()) {
                    item.setValue(item.getValue().divide(t, 2, RoundingMode.HALF_DOWN));
                }
            }
        }
        return ResultResp.success(dataList);
    }


    /**
     * 碳收益预测
     *
     * @param sceneProjectId sceneProjectId
     * @param sceneEdificeId sceneEdificeId
     * @param sceneFloorId   sceneFloorId
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("碳收益预测")
    @GetMapping("carbonRevenueForecast")
    public ResultResp<List<CarbonRevenueForecastResp>> carbonRevenueForecast(@RequestParam(required = false) String
                                                                                     sceneProjectId,
                                                                             @RequestParam(required = false) String sceneEdificeId,
                                                                             @RequestParam(required = false) String sceneFloorId,
                                                                             @RequestParam(required = false) String startTime,
                                                                             @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<CarbonRevenueForecastResp> resps = new ArrayList<>();

//        // 管理后台-数据管理-碳收益预测数据-碳配额/CCER收益预测值
        List<CarbonRevenueForecastDataEntity> forecastDataEntities = carbonRevenueForecastDataService
                .queryForList(sceneProjectId, sceneEdificeId, sceneFloorId, null, start, end);
        Map<String, List<CarbonRevenueForecastDataEntity>> solidifyDateMap = forecastDataEntities.stream()
                .collect(Collectors.groupingBy(item -> DateUtil.format(item.getSolidifyDate(), "yyyy-MM"), TreeMap::new, Collectors.toList()));
        List<CarbonReductionStatisticsResp.DataItem> dataItems = new ArrayList<>();
        List<CarbonReductionStatisticsResp.DataItem> ccerDataItems = new ArrayList<>();
        for (Map.Entry<String, List<CarbonRevenueForecastDataEntity>> entry : solidifyDateMap.entrySet()) {
            BigDecimal value = entry.getValue().stream()
                    .filter(item -> CarbonRevenueForecastDataRevenueTypeEnums.CARBON_QUOTA.getValue().equals(item.getRevenueType())
                            && Objects.nonNull(item.getForecastValue()))
                    .map(CarbonRevenueForecastDataEntity::getForecastValue)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .setScale(2, RoundingMode.HALF_DOWN);
            dataItems.add(new CarbonReductionStatisticsResp.DataItem()
                    .setTime(entry.getKey())
                    .setValue(value));

            BigDecimal ccerValue = entry.getValue().stream()
                    .filter(item -> CarbonRevenueForecastDataRevenueTypeEnums.CCER.getValue().equals(item.getRevenueType())
                            && Objects.nonNull(item.getForecastValue()))
                    .map(CarbonRevenueForecastDataEntity::getForecastValue)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .setScale(2, RoundingMode.HALF_DOWN);
            dataItems.add(new CarbonReductionStatisticsResp.DataItem()
                    .setTime(entry.getKey())
                    .setValue(value));
            ccerDataItems.add(new CarbonReductionStatisticsResp.DataItem()
                    .setTime(entry.getKey())
                    .setValue(ccerValue));
        }
        resps.add(new CarbonRevenueForecastResp().setName("碳配额").setData(dataItems));
        resps.add(new CarbonRevenueForecastResp().setName("CCER").setData(ccerDataItems));
        return ResultResp.success(resps);
    }


    /**
     * 碳价/CCER价格
     *
     * @param sceneProjectId sceneProjectId
     * @param startTime      startTime
     * @param endTime        endTime
     */
    @ApiOperation("碳价/CCER价格")
    @GetMapping("carbonPriceOrccer")
    public ResultResp<List<CarbonPriceOrccerResp>> carbonPriceOrCcer(@RequestParam(required = false) String
                                                                             sceneProjectId,
                                                                     @RequestParam(required = false) String startTime,
                                                                     @RequestParam(required = false) String endTime) {
        LocalDateTime start = StringUtils.hasLength(startTime) ? LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MIN) : null;
        LocalDateTime end = StringUtils.hasLength(endTime) ? LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        List<CarbonPriceOrccerResp> resps = new ArrayList<>();
        List<CarbonPriceDataEntity> priceDataEntities = carbonPriceDataService.list(new LambdaQueryWrapper<CarbonPriceDataEntity>()
                .eq(StringUtils.hasLength(sceneProjectId), CarbonPriceDataEntity::getSceneProjectId, sceneProjectId)
                .ge(Objects.nonNull(start), CarbonPriceDataEntity::getDataTime, start)
                .le(CarbonPriceDataEntity::getDataTime, end)
                .orderByAsc(CarbonPriceDataEntity::getDataTime)
        );
        Map<String, List<CarbonPriceDataEntity>> dataMap = priceDataEntities.stream().collect(Collectors.groupingBy(item -> item.getDataTime()
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), TreeMap::new, Collectors.toList()));
        CarbonPriceOrccerResp priceResp = new CarbonPriceOrccerResp().setName("碳配额");
        List<CarbonPriceOrccerResp.DataItem> priceDataList = new ArrayList<>();
        CarbonPriceOrccerResp ccerResp = new CarbonPriceOrccerResp().setName("CCER");
        List<CarbonPriceOrccerResp.DataItem> ccerDataList = new ArrayList<>();
        for (Map.Entry<String, List<CarbonPriceDataEntity>> entry : dataMap.entrySet()) {
            String date = entry.getKey();
            String time = Integer.valueOf(StringUtils.tokenizeToStringArray(date, "-")[2]) + "日"; // 2024-12-10 --> 10
            CarbonPriceDataEntity priceDataEntity = entry.getValue()
                    .stream()
                    .max(Comparator.comparing(CarbonPriceDataEntity::getDataTime))
                    .get();
            priceDataList.add(new CarbonPriceOrccerResp.DataItem()
                    .setFullDate(date).setTime(time)
                    .setValue(priceDataEntity.getCarbonQuotaPrice()
                            .setScale(2, RoundingMode.HALF_DOWN)));
            ccerDataList.add(new CarbonPriceOrccerResp.DataItem()
                    .setFullDate(date).setTime(time)
                    .setValue(priceDataEntity.getCcerPrice()
                            .setScale(2, RoundingMode.HALF_DOWN)));
        }
        priceResp.setData(priceDataList);
        ccerResp.setData(ccerDataList);
        resps.add(priceResp);
        resps.add(ccerResp);
        return ResultResp.success(resps);
    }


}
